From 0625ef1151c86faa68837e5775ea899b961c22c2 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 10 Dec 2025 23:19:57 +0530 Subject: [PATCH 01/66] feat:new files --- .circleci/config.yml | 172 +++- dev-ops/sample_config.json | 11 + src/configs/kafka.js | 157 +++- .../20230611225246-create-sessions.js | 4 + ...612030911-create-organisation-extension.js | 5 + .../migrations/20230704103100-create-forms.js | 10 + ...session-columns-and-update-mentor-names.js | 6 +- ...20240122130739-add-managersSession-form.js | 2 + ...20240716111210-update-user-id-to-string.js | 8 +- ...208075619-fix-organization-extension-pk.js | 120 +++ ...4559-create-organization-extension-data.js | 45 + ...822124704-add_entity_types_and_entities.js | 3 + .../seeders/20231103090632-seed-forms.js | 6 + src/integration-tests-new/admin/admin.spec.js | 104 +++ .../admin/schemas/admin.schemas.json | 155 ++++ .../cloud-services/cloud-services.spec.js | 47 + .../schemas/cloud-services.schemas.json | 29 + src/integration-tests-new/commonTests.js | 214 +++++ .../config/config.spec.js | 27 + .../config/schemas/config.schemas.json | 58 ++ .../connections/connections.specs.js | 98 ++ .../schemas/connections.schemas.json | 586 ++++++++++++ .../default-rule/default-rule.specs.js | 177 ++++ .../schemas/default-rule.schemas.json | 355 ++++++++ .../entity-type/entity-type.specs.js | 157 ++++ .../schemas/entity-type.schemas.json | 269 ++++++ .../entity/entity.specs.js | 207 +++++ .../entity/schemas/entity.schemas.json | 216 +++++ .../feedback/feedback.spec.js | 70 ++ .../feedback/schemas/feedback.schemas.json | 90 ++ src/integration-tests-new/form/form.specs.js | 172 ++++ .../form/schemas/form.schemas.json | 310 +++++++ .../issues/issues.spec.js | 38 + .../issues/schemas/issues.schemas.json | 37 + .../mentees/mentees.specs.js | 119 +++ .../mentees/schemas/mentees.schemas.json | 247 ++++++ .../mentoring/mentoring.spec.js | 27 + .../mentoring/schemas/mentoring.schemas.json | 71 ++ .../mentors/mentors.specs.js | 143 +++ .../mentors/schemas/mentors.schemas.json | 381 ++++++++ .../modules/modules.spec.js | 113 +++ .../modules/schemas/modules.schemas.json | 152 ++++ .../org-admin/org-admin.spec.js | 203 +++++ .../org-admin/schemas/org-admin.schemas.json | 396 +++++++++ .../permissions/permissions.spec.js | 112 +++ .../schemas/permissions.schemas.json | 180 ++++ .../profile/profile.specs.js | 62 ++ .../profile/schemas/profile.schemas.json | 458 ++++++++++ .../question-set/question-set.spec.js | 96 ++ .../schemas/question-set.schemas.json | 176 ++++ .../questions/questions.spec.js | 124 +++ .../questions/schemas/questions.schemas.json | 293 ++++++ .../report-mapping/report-mapping.spec.js | 106 +++ .../schemas/report-mapping.schemas.json | 237 +++++ .../report-queries/report-queries.spec.js | 94 ++ .../schemas/report-queries.schemas.json | 246 +++++ .../report-type/report-type.spec.js | 104 +++ .../schemas/report-type.schemas.json | 228 +++++ .../reports/reports.spec.js | 184 ++++ .../reports/schemas/reports.schemas.json | 838 ++++++++++++++++++ .../requestSessions/requestSessions.specs.js | 320 +++++++ .../schemas/requestSessions.schemas.json | 540 +++++++++++ .../role-extension/role-extension.spec.js | 133 +++ .../schemas/role-extension.schemas.json | 264 ++++++ .../rolePermissionMapping.spec.js | 75 ++ .../rolePermissionMapping.schemas.json | 72 ++ .../sessions/schemas/sessions.schemas.json | 499 +++++++++++ .../sessions/sessions.specs.js | 596 +++++++++++++ .../users/schemas/users.schemas.json | 100 +++ src/integration-tests-new/users/users.spec.js | 47 + src/integrationJest.config.js | 6 +- src/package.json | 6 +- src/scripts/run-migrations.js | 30 + src/scripts/run-seeders.js | 30 + src/start.sh | 11 + 75 files changed, 12041 insertions(+), 43 deletions(-) create mode 100644 dev-ops/sample_config.json create mode 100644 src/database/migrations/20251208075619-fix-organization-extension-pk.js create mode 100644 src/database/migrations/20251208154559-create-organization-extension-data.js create mode 100644 src/integration-tests-new/admin/admin.spec.js create mode 100644 src/integration-tests-new/admin/schemas/admin.schemas.json create mode 100644 src/integration-tests-new/cloud-services/cloud-services.spec.js create mode 100644 src/integration-tests-new/cloud-services/schemas/cloud-services.schemas.json create mode 100644 src/integration-tests-new/commonTests.js create mode 100644 src/integration-tests-new/config/config.spec.js create mode 100644 src/integration-tests-new/config/schemas/config.schemas.json create mode 100644 src/integration-tests-new/connections/connections.specs.js create mode 100644 src/integration-tests-new/connections/schemas/connections.schemas.json create mode 100644 src/integration-tests-new/default-rule/default-rule.specs.js create mode 100644 src/integration-tests-new/default-rule/schemas/default-rule.schemas.json create mode 100644 src/integration-tests-new/entity-type/entity-type.specs.js create mode 100644 src/integration-tests-new/entity-type/schemas/entity-type.schemas.json create mode 100644 src/integration-tests-new/entity/entity.specs.js create mode 100644 src/integration-tests-new/entity/schemas/entity.schemas.json create mode 100644 src/integration-tests-new/feedback/feedback.spec.js create mode 100644 src/integration-tests-new/feedback/schemas/feedback.schemas.json create mode 100644 src/integration-tests-new/form/form.specs.js create mode 100644 src/integration-tests-new/form/schemas/form.schemas.json create mode 100644 src/integration-tests-new/issues/issues.spec.js create mode 100644 src/integration-tests-new/issues/schemas/issues.schemas.json create mode 100644 src/integration-tests-new/mentees/mentees.specs.js create mode 100644 src/integration-tests-new/mentees/schemas/mentees.schemas.json create mode 100644 src/integration-tests-new/mentoring/mentoring.spec.js create mode 100644 src/integration-tests-new/mentoring/schemas/mentoring.schemas.json create mode 100644 src/integration-tests-new/mentors/mentors.specs.js create mode 100644 src/integration-tests-new/mentors/schemas/mentors.schemas.json create mode 100644 src/integration-tests-new/modules/modules.spec.js create mode 100644 src/integration-tests-new/modules/schemas/modules.schemas.json create mode 100644 src/integration-tests-new/org-admin/org-admin.spec.js create mode 100644 src/integration-tests-new/org-admin/schemas/org-admin.schemas.json create mode 100644 src/integration-tests-new/permissions/permissions.spec.js create mode 100644 src/integration-tests-new/permissions/schemas/permissions.schemas.json create mode 100644 src/integration-tests-new/profile/profile.specs.js create mode 100644 src/integration-tests-new/profile/schemas/profile.schemas.json create mode 100644 src/integration-tests-new/question-set/question-set.spec.js create mode 100644 src/integration-tests-new/question-set/schemas/question-set.schemas.json create mode 100644 src/integration-tests-new/questions/questions.spec.js create mode 100644 src/integration-tests-new/questions/schemas/questions.schemas.json create mode 100644 src/integration-tests-new/report-mapping/report-mapping.spec.js create mode 100644 src/integration-tests-new/report-mapping/schemas/report-mapping.schemas.json create mode 100644 src/integration-tests-new/report-queries/report-queries.spec.js create mode 100644 src/integration-tests-new/report-queries/schemas/report-queries.schemas.json create mode 100644 src/integration-tests-new/report-type/report-type.spec.js create mode 100644 src/integration-tests-new/report-type/schemas/report-type.schemas.json create mode 100644 src/integration-tests-new/reports/reports.spec.js create mode 100644 src/integration-tests-new/reports/schemas/reports.schemas.json create mode 100644 src/integration-tests-new/requestSessions/requestSessions.specs.js create mode 100644 src/integration-tests-new/requestSessions/schemas/requestSessions.schemas.json create mode 100644 src/integration-tests-new/role-extension/role-extension.spec.js create mode 100644 src/integration-tests-new/role-extension/schemas/role-extension.schemas.json create mode 100644 src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js create mode 100644 src/integration-tests-new/rolePermissionMapping/schemas/rolePermissionMapping.schemas.json create mode 100644 src/integration-tests-new/sessions/schemas/sessions.schemas.json create mode 100644 src/integration-tests-new/sessions/sessions.specs.js create mode 100644 src/integration-tests-new/users/schemas/users.schemas.json create mode 100644 src/integration-tests-new/users/users.spec.js create mode 100644 src/scripts/run-migrations.js create mode 100644 src/scripts/run-seeders.js create mode 100755 src/start.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index b9b5ceb95..aecd88282 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,64 +1,174 @@ -version: 2.1 # CircleCI version -orbs: - sonarcloud: sonarsource/sonarcloud@1.1.1 +version: 2.1 + jobs: build: - machine: #Linux machine instead of docker environment + machine: image: ubuntu-2004:2024.05.1 docker_layer_caching: true - working_directory: ~/mentoring # Default working directory + working_directory: ~/mentoring + steps: + - run: + name: Configure git to use HTTPS + command: git config --global url."https://github.com/".insteadOf "git@github.com:" + - checkout: path: ~/mentoring/ + + # Restore mentoring dependencies - restore_cache: key: mentoring-dependency-cache-{{ checksum "src/package.json" }} + - run: name: Install dependencies - command: cd src/ && npm install #cd ../src/ && pwd && + command: cd src/ && npm install + - save_cache: - key: mentoring-dependency-cache-{{checksum "src/package.json"}} + key: mentoring-dependency-cache-{{ checksum "src/package.json" }} paths: - ./src/node_modules + + # Disable SSH rewrite - run: - name: Executing unit test cases - command: cd src/ && npm run test -- --coverage --collectCoverageFrom="./services/**" - - store_artifacts: - path: src/coverage/ - destination: /coverage/ - - sonarcloud/scan - - run: - name: Checking prerequisites - command: |- - docker-compose --version + name: Disable SSH rewrite + command: git config --global --unset url."ssh://git@github.com".insteadOf || true + + # Clone USER service - run: - name: Cloning user service - command: cd ../ && git clone https://github.com/ELEVATE-Project/user.git --branch dev --single-branch + name: Clone User service + command: cd ../ && git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch + - restore_cache: key: user-dependency-cache-{{ checksum "../user/src/package.json" }} + - run: name: Install User service dependencies - command: cd ../user/src/ && npm install #cd ../src/ && pwd && + command: cd ../user/src/ && npm install + - save_cache: - key: user-dependency-cache-{{checksum "../user/src/package.json"}} + key: user-dependency-cache-{{ checksum "../user/src/package.json" }} paths: - ../user/src/node_modules + + # Clone Scheduler service (optional) + - run: + name: Clone Scheduler service + command: cd ../ && git clone https://github.com/ELEVATE-Project/scheduler.git --branch dev --single-branch || true + + # Clone Communications service + - run: + name: Clone Communications service + command: cd ../ && git clone https://github.com/ELEVATE-Project/chat-communications.git --branch dev --single-branch || true + + + - run: + name: Copy config.json into mentoring src + command: cp dev-ops/sample_config.json src/config.json + # ------------------------------- + # FIXED: Start docker-compose (DETACHED) + # ------------------------------- + - run: + name: Start docker containers + command: | + cd dev-ops/ + docker-compose pull || true + docker-compose build || true + docker-compose up -d + sleep 20 + + # ------------------------------- + # FIXED: Wait for services to become healthy + # ------------------------------- + - run: + name: Wait for containers to become healthy + command: | + cd dev-ops/ + echo "Waiting for services to pass healthchecks..." + + for i in {1..30}; do + unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}") + starting=$(docker ps --filter "health=starting" --format "{{.Names}}") + + if [ -z "$unhealthy" ] && [ -z "$starting" ]; then + echo "All services healthy." + break + fi + + echo "Still waiting... (attempt $i)" + docker ps --format "table {{.Names}}\t{{.Status}}" + sleep 5 + done + + echo "Final container status:" + docker ps --format "table {{.Names}}\t{{.Status}}" + + # ------------------------------- + # Diagnostics if services fail + # ------------------------------- + - run: + name: Print logs on failure + when: always + command: | + cd dev-ops/ + echo "Kafka logs:" + docker-compose logs --tail=200 kafka || true + echo "Citus logs:" + docker-compose logs --tail=200 citus || true + echo "Mentoring logs:" + docker-compose logs --tail=200 mentoring || true + echo "User logs:" + docker-compose logs --tail=200 user || true + + + - run: + name: Wait for mentoring MIGRATION_SUCCESS + command: | + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for MIGRATION_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then + echo "Migrations completed." + break + fi + echo "Waiting for migrations... ($i)" + sleep 3 + done + + + + # ------------------------ + # Wait for seeders + # ------------------------ - run: - name: Starting the docker containers - command: |- - cd dev-ops/ && docker-compose up -d + name: Wait for mentoring SEEDER_SUCCESS + command: | + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for SEEDER_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then + echo "Seeders completed." + break + fi + echo "Waiting for seeders... ($i)" + sleep 3 + done + + # ------------------------------- + # Run Integration Tests + # ------------------------------- - run: - name: Running test cases - command: |- - cd src/ && npm run test:integration + name: Running integration tests + command: | + cd src/ + npm run test:integration + - store_test_results: path: ./dev-ops/report + workflows: - build-and-test: # This is the name of the workflow, - # Inside the workflow, you define the jobs you want to run. + build-and-test: jobs: - build: - context: - - SonarCloud filters: tags: only: \b(dev|develop|main)\b diff --git a/dev-ops/sample_config.json b/dev-ops/sample_config.json new file mode 100644 index 000000000..789b6dc22 --- /dev/null +++ b/dev-ops/sample_config.json @@ -0,0 +1,11 @@ +{ + "authTokenUserInformation": { + "id": "data.id", + "name": "data.name", + "organization_code": "data.organizations[?id={{organization_id}}].code", + "organization_id": "data.organization_ids[0]", + "organizations": "data.organizations", + "roles": "data.organizations[?id={{organization_id}}].roles", + "tenant_code": "data.tenant_code" + } +} \ No newline at end of file diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 67b58c3e1..1a5645b7d 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -42,15 +42,33 @@ module.exports = async () => { ) } +/* async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID }) + const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, + heartbeatInterval: 3000, + },) + + // ADD HERE — consumer lifecycle debug logs + consumer.on(consumer.events.HEARTBEAT, () => { + console.log("Kafka Consumer: Heartbeat OK"); + }); + + consumer.on(consumer.events.REBALANCING, (e) => { + console.log(`Kafka Consumer: Rebalancing (messages may be missed) | reason=${e.payload.reason}`); + }); + + consumer.on(consumer.events.GROUP_JOIN, (e) => { + console.log(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload.memberAssignment)}`); + }); await consumer.connect() await consumer.subscribe({ topics: [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) await consumer.run({ - eachMessage: async ({ topic, partition, message }) => { + eachMessage: async ({ topic, partition, message, heartbeat }) => { try { + console.log({ topic, partition, message , heartbeat},'<---{ topic, partition, message }') const rawValue = message.value?.toString() const offset = message.offset if (!rawValue) { @@ -77,7 +95,7 @@ async function startConsumer(kafkaClient) { response = await rolechangeConsumer.messageReceived(payload) } if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) + response = await createuserConsumer.messageReceived(payload,heartbeat) } if (payload.eventType === 'delete') { response = await deleteuserConsumer.messageReceived(payload) @@ -102,6 +120,7 @@ async function startConsumer(kafkaClient) { } logger.info(`Kafk event handling response : ${response}`) } catch (err) { + console.log(err,'err 106') logger.error(`Error in Kafka message handler for topic ${topic}`, { topic, partition, @@ -112,3 +131,135 @@ async function startConsumer(kafkaClient) { }, }) } + +*/ + +async function startConsumer(kafkaClient) { + const consumer = kafkaClient.consumer({ + groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, // allows slow handlers + heartbeatInterval: 3000, + }) + + // Lifecycle logs (important) + consumer.on(consumer.events.GROUP_JOIN, (e) => { + logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) + }) + + consumer.on(consumer.events.REBALANCING, (e) => { + logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) + }) + + consumer.on(consumer.events.HEARTBEAT, () => { + logger.debug('Kafka Consumer: Heartbeat OK') + }) + + await consumer.connect() + logger.info('Kafka Consumer: Connected to broker') + + const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] + + await consumer.subscribe({ topics }) + logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) + + //------------------------------------------------------------------ + // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) + //------------------------------------------------------------------ + await consumer.run({ + autoCommit: true, // safe because processing is fast per message + eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { + logger.info( + `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` + ) + + for (const message of batch.messages) { + if (!isRunning() || isStale()) { + logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') + break + } + + const rawValue = message.value?.toString() + const offset = message.offset + const topic = batch.topic + const partition = batch.partition + + logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) + + if (!rawValue) { + logger.warn(`Kafka Batch: Empty message skipped`) + resolveOffset(offset) + continue + } + + let payload + try { + payload = JSON.parse(rawValue) + } catch (e) { + logger.warn('Kafka Batch: Invalid JSON, skipping', { + offset, + err: e.message, + }) + resolveOffset(offset) + continue + } + + //-------------------------------------------------------- + // ROUTE MESSAGE TO CORRECT HANDLER + //-------------------------------------------------------- + let response + + try { + if (topic === process.env.EVENTS_TOPIC && payload) { + if (payload.eventType === 'roleChange') { + response = await rolechangeConsumer.messageReceived(payload) + } + + if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { + response = await createuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'delete') { + response = await deleteuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { + response = await updateuserConsumer.messageReceived(payload) + } + + // organization events + if ( + payload.entity === 'organization' && + (payload.eventType === 'create' || + payload.eventType === 'update' || + payload.eventType === 'deactivate') + ) { + response = await organizationConsumer.messageReceived(payload) + } + } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { + response = await utils.internalDel(payload.value) + } + + logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) + } catch (handlerErr) { + logger.error(`Kafka Batch: Error handling message`, { + offset, + err: handlerErr.stack || handlerErr.message, + }) + } + + //-------------------------------------------------------- + // MARK MESSAGE AS PROCESSED + //-------------------------------------------------------- + resolveOffset(offset) + + //-------------------------------------------------------- + // HEARTBEAT TO PREVENT TIMEOUT + //-------------------------------------------------------- + await heartbeat() + } + + // commit offsets once per batch + await commitOffsetsIfNecessary() + }, + }) +} diff --git a/src/database/migrations/20230611225246-create-sessions.js b/src/database/migrations/20230611225246-create-sessions.js index 5ccc11d3d..cea6769a0 100644 --- a/src/database/migrations/20230611225246-create-sessions.js +++ b/src/database/migrations/20230611225246-create-sessions.js @@ -123,6 +123,10 @@ module.exports = { custom_entity_text: { type: Sequelize.JSON, }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20230612030911-create-organisation-extension.js b/src/database/migrations/20230612030911-create-organisation-extension.js index e4f6fd6ed..86720761f 100644 --- a/src/database/migrations/20230612030911-create-organisation-extension.js +++ b/src/database/migrations/20230612030911-create-organisation-extension.js @@ -9,6 +9,11 @@ module.exports = { primaryKey: true, type: Sequelize.INTEGER, }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + primaryKey: true, + }, session_visibility_policy: { type: Sequelize.STRING, }, diff --git a/src/database/migrations/20230704103100-create-forms.js b/src/database/migrations/20230704103100-create-forms.js index b5fa05330..efcaf1db1 100644 --- a/src/database/migrations/20230704103100-create-forms.js +++ b/src/database/migrations/20230704103100-create-forms.js @@ -39,6 +39,16 @@ module.exports = { allowNull: false, primaryKey: true, }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: process.env.DEFAULT_TENANT_CODE, + }, + organization_code: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: process.env.DEFAULT_ORGANIZATION_CODE, + }, }) }, diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index d451d8f41..71a53aa7c 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,7 +33,11 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const sessionsWithNullMentorName = await sessionQueries.findAll({ mentor_name: 'Mentor' }) + const defaultTenantCode = process.env.DEFAULT_TENANT_CODE || 'default' + const sessionsWithNullMentorName = await sessionQueries.findAll( + { mentor_name: 'Mentor' }, + defaultTenantCode + ) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') diff --git a/src/database/migrations/20240122130739-add-managersSession-form.js b/src/database/migrations/20240122130739-add-managersSession-form.js index 12f995fcb..45c9fb1ee 100644 --- a/src/database/migrations/20240122130739-add-managersSession-form.js +++ b/src/database/migrations/20240122130739-add-managersSession-form.js @@ -337,6 +337,8 @@ module.exports = { organization_id: defaultOrgId, updated_at: new Date(), created_at: new Date(), + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, }, ], {} diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8aaa64c97..8baafbae6 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - await queryInterface.changeColumn('session_ownerships', 'user_id', { - type: Sequelize.STRING, - allowNull: false, - }) + // await queryInterface.changeColumn('session_ownerships', 'user_id', { + // type: Sequelize.STRING, + // allowNull: false, + // }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251208075619-fix-organization-extension-pk.js b/src/database/migrations/20251208075619-fix-organization-extension-pk.js new file mode 100644 index 000000000..bd0e82a4d --- /dev/null +++ b/src/database/migrations/20251208075619-fix-organization-extension-pk.js @@ -0,0 +1,120 @@ +'use strict' + +module.exports = { + async up(queryInterface, Sequelize) { + const table = 'organization_extension' + + // + // 1. Ensure new columns exist (safety – avoids failures in older DBs) + // + const ensureColumn = async (column, type) => { + const exists = await queryInterface.sequelize.query( + ` + SELECT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = '${table}' AND column_name = '${column}' + ) as exists; + `, + { type: Sequelize.QueryTypes.SELECT } + ) + + if (!exists[0].exists) { + await queryInterface.addColumn(table, column, { type, allowNull: true }) + } + } + + await ensureColumn('organization_code', Sequelize.STRING) + await ensureColumn('tenant_code', Sequelize.STRING) + + // + // 2. Backfill NULL values (required before NOT NULL constraint) + // + await queryInterface.sequelize.query( + ` + UPDATE ${table} + SET organization_code = 'default_org' + WHERE organization_code IS NULL; + ` + ) + + await queryInterface.sequelize.query( + ` + UPDATE ${table} + SET tenant_code = 'default_tenant' + WHERE tenant_code IS NULL; + ` + ) + + // + // 3. Apply NOT NULL constraints + // + await queryInterface.changeColumn(table, 'organization_code', { + type: Sequelize.STRING, + allowNull: false, + }) + + await queryInterface.changeColumn(table, 'tenant_code', { + type: Sequelize.STRING, + allowNull: false, + }) + + await queryInterface.changeColumn(table, 'organization_id', { + type: Sequelize.STRING, + allowNull: false, + }) + + // + // 4. Drop existing primary key (if exists) + // + const pkNameQuery = await queryInterface.sequelize.query( + ` + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name='${table}' AND constraint_type='PRIMARY KEY'; + `, + { type: Sequelize.QueryTypes.SELECT } + ) + + if (pkNameQuery.length > 0) { + const pkName = pkNameQuery[0].constraint_name + await queryInterface.sequelize.query(`ALTER TABLE ${table} DROP CONSTRAINT ${pkName};`) + } + + // + // 5. Add composite primary key + // + await queryInterface.sequelize.query( + ` + ALTER TABLE ${table} + ADD PRIMARY KEY (organization_id, organization_code, tenant_code); + ` + ) + }, + + async down(queryInterface, Sequelize) { + const table = 'organization_extension' + + // Drop composite PK + const pkNameQuery = await queryInterface.sequelize.query( + ` + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name='${table}' AND constraint_type='PRIMARY KEY'; + `, + { type: Sequelize.QueryTypes.SELECT } + ) + + if (pkNameQuery.length > 0) { + const pkName = pkNameQuery[0].constraint_name + await queryInterface.sequelize.query(`ALTER TABLE ${table} DROP CONSTRAINT ${pkName};`) + } + + // Restore old single-column PK (optional — depends on original schema) + await queryInterface.sequelize.query( + ` + ALTER TABLE ${table} + ADD PRIMARY KEY (organization_id); + ` + ) + }, +} diff --git a/src/database/migrations/20251208154559-create-organization-extension-data.js b/src/database/migrations/20251208154559-create-organization-extension-data.js new file mode 100644 index 000000000..cded4809b --- /dev/null +++ b/src/database/migrations/20251208154559-create-organization-extension-data.js @@ -0,0 +1,45 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.bulkInsert('organization_extension', [ + { + organization_id: process.env.DEFAULT_ORG_ID, + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, + + session_visibility_policy: 'CURRENT', + mentor_visibility_policy: 'CURRENT', + external_session_visibility_policy: 'CURRENT', + external_mentor_visibility_policy: 'CURRENT', + + approval_required_for: Sequelize.literal(`ARRAY[]::text[]`), // corrected from CSV `{}` → `[]` + allow_mentor_override: false, + + created_by: '1', + updated_by: '1', + + mentee_feedback_question_set: 'MENTEE_QS1', + mentor_feedback_question_set: 'MENTOR_QS2', + + uploads: null, + mentee_visibility_policy: 'CURRENT', + external_mentee_visibility_policy: 'CURRENT', + name: null, + theme: null, + deleted_at: null, + created_at: new Date('2025-12-08T15:34:21.220Z'), + updated_at: new Date('2025-12-08T15:34:21.220Z'), + }, + ]) + }, + + async down(queryInterface, Sequelize) { + await queryInterface.bulkDelete('organization_extension', { + organization_id: '1', + organization_code: 'default_code', + tenant_code: 'default', + }) + }, +} diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index 5ed8fcadc..e61fd703c 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,6 +120,8 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -153,6 +155,7 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 + eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE entitiesFinalArray.push(eachEntity) }) diff --git a/src/database/seeders/20231103090632-seed-forms.js b/src/database/seeders/20231103090632-seed-forms.js index 226453e9a..ffd7441f8 100644 --- a/src/database/seeders/20231103090632-seed-forms.js +++ b/src/database/seeders/20231103090632-seed-forms.js @@ -920,6 +920,12 @@ module.exports = { created_at: new Date(), }, ] + + formData.forEach((formInstance) => { + formInstance.tenant_code = process.env.DEFAULT_TENANT_CODE + formInstance.organization_code = process.env.DEFAULT_ORGANIZATION_CODE + }) + await queryInterface.bulkInsert('forms', formData, {}) } catch (error) { console.error('Error seeding forms:', error) diff --git a/src/integration-tests-new/admin/admin.spec.js b/src/integration-tests-new/admin/admin.spec.js new file mode 100644 index 000000000..72a674c9c --- /dev/null +++ b/src/integration-tests-new/admin/admin.spec.js @@ -0,0 +1,104 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/admin.schemas.json') + +describe('admin endpoints generated from api-doc.yaml', () => { + describe('DELETE /mentoring/v1/admin/userDelete', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/admin/userDelete` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_admin_userDelete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/admin/triggerViewRebuild', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/admin/triggerViewRebuild` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_admin_triggerViewRebuild'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/admin/triggerViewRebuildInternal', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/admin/triggerViewRebuildInternal` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_admin_triggerViewRebuildInternal'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/admin/triggerPeriodicViewRefresh', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/admin/triggerPeriodicViewRefresh` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_admin_triggerPeriodicViewRefresh'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/admin/triggerPeriodicViewRefreshInternal', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/admin/triggerPeriodicViewRefreshInternal` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_admin_triggerPeriodicViewRefreshInternal'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/admin/schemas/admin.schemas.json b/src/integration-tests-new/admin/schemas/admin.schemas.json new file mode 100644 index 000000000..fd10a0a54 --- /dev/null +++ b/src/integration-tests-new/admin/schemas/admin.schemas.json @@ -0,0 +1,155 @@ +{ + "DELETE_mentoring_v1_admin_userDelete": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "isAttendeesNotified": { + "type": "boolean" + }, + "areUserDetailsCleared": { + "type": "boolean" + }, + "isUnenrolledFromSessions": { + "type": "boolean" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_admin_triggerViewRebuild": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_admin_triggerViewRebuildInternal": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_admin_triggerPeriodicViewRefresh": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_admin_triggerPeriodicViewRefreshInternal": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/cloud-services/cloud-services.spec.js b/src/integration-tests-new/cloud-services/cloud-services.spec.js new file mode 100644 index 000000000..2c1208f67 --- /dev/null +++ b/src/integration-tests-new/cloud-services/cloud-services.spec.js @@ -0,0 +1,47 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/cloud-services.schemas.json') + +describe('cloud-services endpoints generated from api-doc.yaml', () => { + describe('GET /mentoring/v1/cloud-services/getSignedUrl', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/cloud-services/getSignedUrl` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_cloud-services_getSignedUrl'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/cloud-services/getDownloadableUrl', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/cloud-services/getDownloadableUrl` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_cloud-services_getDownloadableUrl'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/cloud-services/schemas/cloud-services.schemas.json b/src/integration-tests-new/cloud-services/schemas/cloud-services.schemas.json new file mode 100644 index 000000000..f06891a7e --- /dev/null +++ b/src/integration-tests-new/cloud-services/schemas/cloud-services.schemas.json @@ -0,0 +1,29 @@ +{ + "GET_mentoring_v1_cloud-services_getSignedUrl": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "signed_url": { + "type": "string" + }, + "file_path": { + "type": "string" + }, + "dest_file_path": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_cloud-services_getDownloadableUrl": {} +} diff --git a/src/integration-tests-new/commonTests.js b/src/integration-tests-new/commonTests.js new file mode 100644 index 000000000..bc377074e --- /dev/null +++ b/src/integration-tests-new/commonTests.js @@ -0,0 +1,214 @@ +var supertest = require('supertest') //require supertest +var defaults = require('superagent-defaults') +const { faker } = require('@faker-js/faker') +const crypto = require('crypto') +const common = require('@constants/common') +let baseURL = 'http://localhost:3000' +//supertest hits the HTTP server (your app) +let defaultHeaders +let admin_secret_code = process.env.ADMIN_SECRET_CODE || 'ADMIN_SECRET_CODE' + +const logIn = async () => { + try { + let request = defaults(supertest('http://localhost:3001')) + let waitOn = require('wait-on') + let opts = { + resources: [baseURL], + delay: 1000, // initial delay in ms, default 0 + interval: 1000, // poll interval in ms, default 250ms + timeout: 60000, + } + await waitOn(opts) + let email = 'adithya' + crypto.randomBytes(5).toString('hex') + '@tunerlabs.com' + let password = faker.internet.password() + let res = await request.post('/user/v1/account/create').set('origin', 'localhost').send({ + name: 'adithya', + email: email, + password: 'PassworD@@@123', + role: common.MENTEE_ROLE, + }) + + res = await request.post('/user/v1/account/login').set('origin', 'localhost').send({ + identifier: email, + password: 'PassworD@@@123', + }) + + await waitForProfileExtension(res.body.result.access_token) + + if (res.body.result.access_token && res.body.result.user.id) { + defaultHeaders = { + 'x-auth-token': 'bearer ' + res.body.result.access_token, + Connection: 'keep-alive', + 'Content-Type': 'application/json', + } + global.request = defaults(supertest(baseURL)) + global.request.set(defaultHeaders) + global.userId = res.body.result.user.id + + const userDetails = { + token: res.body.result.access_token, + refreshToken: res.body.result.refresh_token, + userId: res.body.result.user.id, + email: email, + password: password, + organizations: res.body.result.user.organizations, + } + + return userDetails + } else { + console.error('Error while getting access token') + return false + } + } catch (error) { + console.error(error) + } +} +const mentorLogIn = async () => { + try { + let request = defaults(supertest('http://localhost:3001')) + var waitOn = require('wait-on') + var opts = { + resources: [baseURL], + delay: 1000, // initial delay in ms, default 0 + interval: 500, // poll interval in ms, default 250ms + timeout: 30000, + } + await waitOn(opts) + let email = 'nevil' + crypto.randomBytes(5).toString('hex') + '@tunerlabs.com' + let password = faker.internet.password() + + let res = await request.post('/user/v1/account/create').set('origin', 'localhost').send({ + name: 'Nevil', + email: email, + password: 'PassworD@@@123', + isAMentor: true, + secretCode: 'secret-code', + }) + + console.log(res.body, '<-- 87') + + res = await request.post('/user/v1/account/login').set('origin', 'localhost').send({ + identifier: email, + password: 'PassworD@@@123', + }) + await waitForProfileExtension(res.body.result.access_token) + if (res.body.result.access_token && res.body.result.user.id) { + defaultHeaders = { + 'x-auth-token': 'bearer ' + res.body.result.access_token, + Connection: 'keep-alive', + 'Content-Type': 'application/json', + } + global.request = defaults(supertest(baseURL)) + global.request.set(defaultHeaders) + global.userId = res.body.result.user.id + + const mentorDetails = { + token: res.body.result.access_token, + refreshToken: res.body.result.refresh_token, + userId: res.body.result.user.id, + email: email, + password: password, + organizations: res.body.result.user.organizations, + } + + return mentorDetails + } else { + console.error('Error while getting access token') + return false + } + } catch (error) { + console.error(error) + } +} +const adminLogin = async () => { + try { + let request = defaults(supertest('http://localhost:3001')) + var waitOn = require('wait-on') + var opts = { + resources: [baseURL], + delay: 1000, // initial delay in ms, default 0 + interval: 500, // poll interval in ms, default 250ms + timeout: 30000, + } + await waitOn(opts) + let email = 'system' + crypto.randomBytes(5).toString('hex') + '@admin.com' + let password = 'PASSword###11' + + let adminCreate = await request + .post('/user/v1/admin/create') + .set('internal_access_token', 'internal_access_token') //NOTE: Please replace {{internal_access_token}} with your actual token + .send({ + name: 'system', + email: email, + password: password, + secret_code: admin_secret_code, + }) + + let res = await request.post('/user/v1/admin/login').set('origin', 'localhost').send({ + identifier: email, + password: password, + }) + await waitForProfileExtension(res.body.result.access_token) + if (res.body.result.access_token && res.body.result.user.id) { + defaultHeaders = { + 'x-auth-token': 'bearer ' + res.body.result.access_token, + Connection: 'keep-alive', + 'Content-Type': 'application/json', + } + global.request = defaults(supertest(baseURL)) + global.request.set(defaultHeaders) + global.userId = res.body.result.user.id + + const adminDetails = { + token: res.body.result.access_token, + refreshToken: res.body.result.refresh_token, + userId: res.body.result.user.id, + email: email, + password: password, + organizations: res.body.result.user.organizations, + } + + return adminDetails + } else { + console.error('Error while getting access token') + return false + } + } catch (error) { + console.error(error) + } +} +function logError(res) { + let successCodes = [200, 201, 202] + if (!successCodes.includes(res.statusCode)) { + console.log('Response Body', res.body) + } +} + +const waitForProfileExtension = async (token, timeoutMs = 15000) => { + const start = Date.now() + + while (Date.now() - start < timeoutMs) { + try { + let request = defaults(supertest('http://localhost:3000')) + const res = await request + .get(`/mentoring/v1/profile/getExtension`) + .set('x-auth-token', token) + .set('origin', 'localhost') + + if (res.status === 200 && res.body && res.body.result) { + return res.body.result + } + } catch (error) {} + + await new Promise((r) => setTimeout(r, 300)) + } + + throw new Error('Profile extension not ready within timeout') +} + +module.exports = { + logIn, //-- export if token is generated + logError, + mentorLogIn, + adminLogin, +} diff --git a/src/integration-tests-new/config/config.spec.js b/src/integration-tests-new/config/config.spec.js new file mode 100644 index 000000000..aea2738b2 --- /dev/null +++ b/src/integration-tests-new/config/config.spec.js @@ -0,0 +1,27 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/config.schemas.json') + +describe('config endpoints generated from api-doc.yaml', () => { + describe('GET /mentoring/v1/config/get', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/config/get` + let req = request(BASE).get(url) + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_config_get'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/config/schemas/config.schemas.json b/src/integration-tests-new/config/schemas/config.schemas.json new file mode 100644 index 000000000..2da66807b --- /dev/null +++ b/src/integration-tests-new/config/schemas/config.schemas.json @@ -0,0 +1,58 @@ +{ + "GET_mentoring_v1_config_get": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "meeting_platform": { + "type": "string" + }, + "report_issue": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "subject": { + "type": "string" + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "forms_version": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + } + } + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/connections/connections.specs.js b/src/integration-tests-new/connections/connections.specs.js new file mode 100644 index 000000000..d7325eb67 --- /dev/null +++ b/src/integration-tests-new/connections/connections.specs.js @@ -0,0 +1,98 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let adminDetails = null + +const schemas = require('./schemas/connections.schemas.json') + +beforeAll(async () => { + adminDetails = await commonHelper.adminLogin() +}) + +describe('connections endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/connections/initiate', () => { + test('should return 200', async () => { + const menteeDetails = await commonHelper.logIn() + const mentorDetails = await commonHelper.mentorLogIn() + + const url = `/mentoring/v1/connections/initiate` + let req = request(BASE).post(url) + req = req.set('x-auth-token', menteeDetails.token) // Mentee initiates + req = req + .send({ + user_id: mentorDetails.userId.toString(), // With mentor + message: 'Hi, I would like to connect with you.', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_mentoring_v1_connections_initiate'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('POST /mentoring/v1/connections/accept', () => { + test('should return 200', async () => { + const menteeDetails = await commonHelper.logIn() + const mentorDetails = await commonHelper.mentorLogIn() + // Step 1: Mentee initiates a connection with Mentor + const initiateRes = await request(BASE) + .post('/mentoring/v1/connections/initiate') + .set('x-auth-token', menteeDetails.token) + .send({ + user_id: mentorDetails.userId.toString(), + message: 'Hi, please accept my connection request.', + }) + expect(initiateRes.status).toBe(201) + + // Step 2: Mentor accepts the connection from Mentee + const url = `/mentoring/v1/connections/accept` + let req = request(BASE).post(url) + req = req.set('x-auth-token', mentorDetails.token) // Mentor accepts + req = req + .send({ + user_id: menteeDetails.userId.toString(), // From mentee + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_mentoring_v1_connections_accept'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('POST /mentoring/v1/connections/reject', () => { + test('should return 200', async () => { + const menteeDetails = await commonHelper.logIn() + const mentorDetails = await commonHelper.mentorLogIn() + // Step 1: Mentee initiates a connection with Mentor + const initiateRes = await request(BASE) + .post('/mentoring/v1/connections/initiate') + .set('x-auth-token', menteeDetails.token) + .send({ + user_id: mentorDetails.userId.toString(), + message: 'Hi, I am sending a request to be rejected.', + }) + expect(initiateRes.status).toBe(201) + + // Step 2: Mentor rejects the connection from Mentee + const url = `/mentoring/v1/connections/reject` + let req = request(BASE).post(url) + req = req.set('x-auth-token', mentorDetails.token) // Mentor rejects + req = req + .send({ + user_id: menteeDetails.userId.toString(), // From mentee + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_mentoring_v1_connections_reject'] + expect(res.body).toMatchSchema(schema) + }) + }) +}) diff --git a/src/integration-tests-new/connections/schemas/connections.schemas.json b/src/integration-tests-new/connections/schemas/connections.schemas.json new file mode 100644 index 000000000..5b1e2f013 --- /dev/null +++ b/src/integration-tests-new/connections/schemas/connections.schemas.json @@ -0,0 +1,586 @@ +{ + "POST_mentoring_v1_connections_initiate": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "id": { + "type": "number" + }, + "user_id": { + "type": "string" + }, + "friend_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_connections_pending": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "user_id": { + "type": "string" + }, + "friend_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "updated_by": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "user_details": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "mentee_visibility": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "designation": { + "type": "null" + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "string" + }, + "experience": { + "type": "null" + }, + "is_mentor": { + "type": "boolean" + }, + "image": { + "type": "string" + }, + "communications_user_id": { + "type": "string" + } + } + } + } + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_connections_list": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "mentee_visibility": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "designation": { + "type": "null" + }, + "experience": { + "type": "null" + }, + "is_mentor": { + "type": "boolean" + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "string" + }, + "image": { + "type": "string" + }, + "communications_user_id": { + "type": "string" + } + } + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_connections_getInfo": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "user_id": { + "type": "string" + }, + "friend_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "room_id": { + "type": "string" + } + } + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "updated_by": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "user_details": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "mentee_visibility": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "designation": { + "type": "null" + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "string" + }, + "is_mentor": { + "type": "boolean" + }, + "experience": { + "type": "null" + }, + "image": { + "type": "string" + }, + "communications_user_id": { + "type": "string" + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_connections_reject": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_connections_accept": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "user_id": { + "type": "string" + }, + "friend_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "room_id": { + "type": "string" + } + } + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "updated_by": { + "type": "string" + }, + "created_by": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/default-rule/default-rule.specs.js b/src/integration-tests-new/default-rule/default-rule.specs.js new file mode 100644 index 000000000..9399c6c56 --- /dev/null +++ b/src/integration-tests-new/default-rule/default-rule.specs.js @@ -0,0 +1,177 @@ +jest.setTimeout(30000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const commonHelper = require('@commonTests') + +const schemas = require('./schemas/default-rule.schemas.json') + +let adminDetails = null +let testEntityTypeValue = null +let createdRuleId = null + +beforeAll(async () => { + adminDetails = await commonHelper.adminLogin() + + // Create a unique entity type for this test run + //make it unique using random alphabets not numbers + testEntityTypeValue = 'entityTypeForRule' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const createEntityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .send({ + value: testEntityTypeValue, + label: 'Test Entity Type for Rules', + data_type: 'STRING', + model_names: ['UserExtension'], + required: true, // As per your requirement + allow_filtering: true, + }) + expect(createEntityTypeRes.status).toBe(201) +}) + +describe('default-rule endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/default-rule/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/default-rule/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req + .send({ + type: 'session', + target_field: testEntityTypeValue, + is_target_from_sessions_mentor: true, + requester_field: testEntityTypeValue, + operator: 'equals', + requester_roles: ['session_manager'], + requester_roles_config: { + exclude: true, + }, + }) + .set('Content-Type', 'application/json') + + const res = await req + expect(res.status).toBe(201) + createdRuleId = res.body.result.id // Save the ID for update/delete tests + expect(res.status).toBe(201) + // validate response schema + expect(res.body).toMatchSchema(schemas['POST_mentoring_v1_default-rule_create']) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/default-rule/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/default-rule/read', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/default-rule/read` + const res = await request(BASE).get(url).set('x-auth-token', adminDetails.token) + expect(res.status).toBe(200) + // validate response schema + expect(res.body).toMatchSchema(schemas['GET_mentoring_v1_default-rule_read']) + }) + }) + + describe('PATCH /mentoring/v1/default-rule/update/{id}', () => { + test('should return 200 on successful update', async () => { + // Step 1: Create a unique entity type for the update test + const entityTypeForUpdate = + 'entityTypeForUpdate' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const createEntityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .send({ + value: entityTypeForUpdate, + label: 'Test Entity Type for Rule Update', + data_type: 'STRING', + model_names: ['UserExtension'], + required: true, + allow_filtering: true, + }) + expect(createEntityTypeRes.status).toBe(201) + + // Step 2: Create a default rule to be updated + const createRuleRes = await request(BASE) + .post('/mentoring/v1/default-rule/create') + .set('x-auth-token', adminDetails.token) + .send({ + type: 'session', + target_field: entityTypeForUpdate, + is_target_from_sessions_mentor: true, + requester_field: entityTypeForUpdate, + operator: 'equals', + requester_roles: ['session_manager'], + requester_roles_config: { exclude: true }, + }) + expect(createRuleRes.status).toBe(201) + const ruleIdToUpdate = createRuleRes.body.result.id + + // Step 3: Update the created rule + const url = `/mentoring/v1/default-rule/update/${ruleIdToUpdate}` + let req = request(BASE).patch(url).set('x-auth-token', adminDetails.token) + req = req + .send({ + type: 'session', + target_field: entityTypeForUpdate.toLocaleLowerCase(), + requester_field: entityTypeForUpdate.toLocaleLowerCase(), + // Using a different operator for update + operator: 'notEquals', + requester_roles_config: { exclude: false }, + is_target_from_sessions_mentor: true, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(202) + // validate response schema + expect(res.body).toMatchSchema(schemas['PATCH_mentoring_v1_default-rule_update_id']) + }) + }) + + describe('DELETE /mentoring/v1/default-rule/delete/{id}', () => { + test('should return 200 on successful deletion', async () => { + // Step 1: Create a unique entity type for the delete test + const entityTypeForDelete = + 'entityTypeForDelete' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const createEntityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .send({ + value: entityTypeForDelete, + label: 'Test Entity Type for Rule Deletion', + data_type: 'STRING', + model_names: ['UserExtension'], + required: true, + allow_filtering: true, + }) + expect(createEntityTypeRes.status).toBe(201) + + // Step 2: Create a default rule to be deleted + const createRuleRes = await request(BASE) + .post('/mentoring/v1/default-rule/create') + .set('x-auth-token', adminDetails.token) + .send({ + type: 'session', + target_field: entityTypeForDelete, + is_target_from_sessions_mentor: true, + requester_field: entityTypeForDelete, + operator: 'equals', + requester_roles: ['session_manager'], + requester_roles_config: { exclude: true }, + }) + expect(createRuleRes.status).toBe(201) + const ruleIdToDelete = createRuleRes.body.result.id + + // Step 3: Delete the created rule + const url = `/mentoring/v1/default-rule/delete/${ruleIdToDelete}` + const res = await request(BASE).delete(url).set('x-auth-token', adminDetails.token) + expect(res.status).toBe(202) + // validate response schema + expect(res.body).toMatchSchema(schemas['DELETE_mentoring_v1_default-rule_delete_id']) + }) + }) +}) diff --git a/src/integration-tests-new/default-rule/schemas/default-rule.schemas.json b/src/integration-tests-new/default-rule/schemas/default-rule.schemas.json new file mode 100644 index 000000000..a42b6b79f --- /dev/null +++ b/src/integration-tests-new/default-rule/schemas/default-rule.schemas.json @@ -0,0 +1,355 @@ +{ + "POST_mentoring_v1_default-rule_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "target_field": { + "type": "string" + }, + "is_target_from_sessions_mentor": { + "type": "boolean" + }, + "requester_field": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "requester_roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "requester_roles_config": { + "type": "object", + "properties": { + "exclude": { + "type": "boolean" + } + } + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "field_configs": { + "type": "null" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_default-rule_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "target_field": { + "type": "string" + }, + "is_target_from_sessions_mentor": { + "type": "boolean" + }, + "requester_field": { + "type": "string" + }, + "field_configs": { + "type": "null" + }, + "matching_operator": { + "type": "string" + }, + "requester_roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "role_config": { + "type": "object", + "properties": { + "exclude": { + "type": "boolean" + } + } + }, + "organization_id": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "PATCH_mentoring_v1_default-rule_update_id": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "target_field": { + "type": "string" + }, + "is_target_from_sessions_mentor": { + "type": "boolean" + }, + "requester_field": { + "type": "string" + }, + "field_configs": { + "type": "null" + }, + "operator": { + "type": "string" + }, + "requester_roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "requester_roles_config": { + "type": "object", + "properties": { + "exclude": { + "type": "boolean" + } + } + }, + "organization_id": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_default-rule_delete_id": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/entity-type/entity-type.specs.js b/src/integration-tests-new/entity-type/entity-type.specs.js new file mode 100644 index 000000000..d8d7f30bc --- /dev/null +++ b/src/integration-tests-new/entity-type/entity-type.specs.js @@ -0,0 +1,157 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let menteeDetails = null // This user will make the request +let mentorDetails = null // This user will be the requestee +let adminDetails = null // Admin user if needed + +const schemas = require('./schemas/entity-type.schemas.json') + +beforeAll(async () => { + console.log('setting up global variables....') + // Log in both a mentee and a mentor for the test + menteeDetails = await commonHelper.logIn() + mentorDetails = await commonHelper.mentorLogIn() + adminDetails = await commonHelper.adminLogin() +}) + +describe('entity-type endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/entity-type/create', () => { + test('should return 201', async () => { + let value = 'string' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + + console.log('Creating entity type with value:', value) + const url = `/mentoring/v1/entity-type/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + req = req.set('timezone', 'Asia/Calcutta') + req = req + .send({ + value: value, // need unique value so concatenate with more characters which are alphabets and not numbers + label: 'String', + allow_filtering: true, + data_type: 'STRING', + model_names: ['UserExtension'], + required: true, + status: 'string', + type: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_mentoring_v1_entity-type_create'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('GET /mentoring/v1/entity-type/read', () => { + // Note: The endpoint name suggests GET, but the implementation requires POST. + test('should return 200 on success', async () => { + const url = `/mentoring/v1/entity-type/read` + let req = request(BASE).post(url) + req = req.set('x-auth-token', mentorDetails.token) + req = req.set('org-id', mentorDetails.organizations[0].id.toString()) + req = req.set('timezone', 'Asia/Calcutta') + // This endpoint expects a body with the values to read, similar to the QA curl command. + req = req.send({ value: ['string', 'designation'] }) + const res = await req + expect(res.status).toBe(200) + // validate response schema + const schema = schemas['GET_mentoring_v1_entity-type_read'] + if (!schema) throw new Error('Schema not found for GET_mentoring_v1_entity-type_read') + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('POST /mentoring/v1/entity-type/update/{id}', () => { + test('should return 200 on success', async () => { + //const url = `/mentoring/v1/entity-type/update/1` // Use a real or placeholder ID + // First, create an entity type to update + const value = 'updateTestEntity' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const createRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: value, + label: 'Test Label', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + + expect(createRes.status).toBe(201) + const entityTypeId = createRes.body.result.id + + // Now, update the created entity type + const url = `/mentoring/v1/entity-type/update/${entityTypeId}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + req = req.set('timezone', 'Asia/Calcutta') + + // Update payload + req = req + .send({ + value: value, + label: 'string', + status: 'INACTIVE', + type: 'string', + data_type: 'STRING', + model_names: ['Session'], + allow_filtering: true, + required: true, + }) + .set('Content-Type', 'application/json') + const res = await req + //change to greater than equal to 202 exact + expect(res.status).toBe(202) + + // validate response schema + if (!schemas['POST_mentoring_v1_entity-type_update_id']) + throw new Error('Schema not found for POST_mentoring_v1_entity-type_update_id') + const schema = schemas['POST_mentoring_v1_entity-type_update_id'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('DELETE /mentoring/v1/entity-type/delete/{id}', () => { + test('should return 200 on success', async () => { + //const url = `/mentoring/v1/entity-type/delete/1` // Use a real or placeholder ID + // First, create an entity type to delete + const value = 'deleteTestEntity' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const createRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: value, + label: 'Test Label for Deletion', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + + expect(createRes.status).toBe(201) + const entityTypeId = createRes.body.result.id + + // Now, delete the created entity type + const url = `/mentoring/v1/entity-type/delete/${entityTypeId}` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + req = req.set('timezone', 'Asia/Calcutta') + const res = await req + expect(res.status).toBe(202) + // validate response schema + if (!schemas['DELETE_mentoring_v1_entity-type_delete_id']) + throw new Error('Schema not found for DELETE_mentoring_v1_entity-type_delete_id') + const schema = schemas['DELETE_mentoring_v1_entity-type_delete_id'] + expect(res.body).toMatchSchema(schema) + }) + }) +}) diff --git a/src/integration-tests-new/entity-type/schemas/entity-type.schemas.json b/src/integration-tests-new/entity-type/schemas/entity-type.schemas.json new file mode 100644 index 000000000..83b30c74f --- /dev/null +++ b/src/integration-tests-new/entity-type/schemas/entity-type.schemas.json @@ -0,0 +1,269 @@ +{ + "POST_mentoring_v1_entity-type_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "allow_filtering": { + "type": "boolean" + }, + "data_type": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "has_entities": { + "type": "boolean" + } + } + }, + "field_0": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_entity-type_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "entity_types": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "value": { "type": "string" }, + "label": { "type": "string" }, + "status": { "type": "string" }, + "created_by": { "type": "string" }, + "updated_by": { "type": "string" }, + "allow_filtering": { "type": "boolean" }, + "data_type": { "type": "string" }, + "organization_id": { "type": "string" }, + "parent_id": { "type": ["null", "number"] }, + "allow_custom_entities": { "type": "boolean" }, + "has_entities": { "type": "boolean" }, + "model_names": { + "type": "array", + "items": { "type": "string" } + }, + "required": { "type": "boolean" }, + "regex": { "type": "null" }, + "report_filter": { "type": "boolean" }, + "meta": { "type": "null" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "deleted_at": { "type": "null" }, + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "entity_type_id": { "type": "number" }, + "value": { "type": "string" }, + "label": { "type": "string" }, + "status": { "type": "string" }, + "type": { "type": "string" }, + "created_by": { "type": "string" }, + "updated_by": { "type": ["null", "string"] }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "deleted_at": { "type": "null" } + }, + "required": [ + "id", + "entity_type_id", + "value", + "label", + "status", + "type", + "created_by", + "updated_by", + "created_at", + "updated_at", + "deleted_at" + ] + } + } + }, + "required": [ + "id", + "value", + "label", + "status", + "created_by", + "updated_by", + "allow_filtering", + "data_type", + "organization_id", + "parent_id", + "allow_custom_entities", + "has_entities", + "model_names", + "required", + "regex", + "report_filter", + "meta", + "created_at", + "updated_at", + "deleted_at", + "entities" + ] + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_entity-type_update_id": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "value": { "type": "string" }, + "label": { "type": "string" }, + "status": { "type": "string" }, + "created_by": { "type": "string" }, + "updated_by": { "type": "string" }, + "allow_filtering": { "type": "boolean" }, + "data_type": { "type": "string" }, + "organization_id": { "type": "string" }, + "parent_id": { "type": "null" }, + "allow_custom_entities": { "type": "boolean" }, + "has_entities": { "type": "boolean" }, + "model_names": { + "type": "array", + "items": { "type": "string" } + }, + "required": { "type": "boolean" }, + "regex": { "type": "null" }, + "report_filter": { "type": "boolean" }, + "meta": { "type": "null" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "deleted_at": { "type": "null" } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { "type": "string" }, + "meetingPlatform": { "type": "string" } + } + } + } + }, + "DELETE_mentoring_v1_entity-type_delete_id": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/entity/entity.specs.js b/src/integration-tests-new/entity/entity.specs.js new file mode 100644 index 000000000..70016a6c2 --- /dev/null +++ b/src/integration-tests-new/entity/entity.specs.js @@ -0,0 +1,207 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let menteeDetails = null +let mentorDetails = null +let adminDetails = null +const schemas = require('./schemas/entity.schemas.json') + +beforeAll(async () => { + menteeDetails = await commonHelper.logIn() + mentorDetails = await commonHelper.mentorLogIn() + adminDetails = await commonHelper.adminLogin() +}) + +describe('entity endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/entity/create', () => { + test('should return 201', async () => { + // First, create an entity-type to associate the entity with + let entityValue = 'newEntityType' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const entityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: entityValue, + label: 'Test Entity Type', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + expect(entityTypeRes.status).toBe(201) + const entityTypeId = entityTypeRes.body.result.id + + const url = `/mentoring/v1/entity/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + req = req + .send({ + value: 'en', + label: 'English', + entity_type_id: entityTypeId, + type: 'SYSTEM', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_/mentoring/v1/entity/create'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/entity/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('PUT /mentoring/v1/entity/update/{id}', () => { + test('should return 202', async () => { + let value = 'updateEntity' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + // Create entity type + const entityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: value, + label: 'Update Test', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + + expect(entityTypeRes.status).toBe(201) + const entityTypeId = entityTypeRes.body.result.id + + // Create entity + const entityRes = await request(BASE) + .post('/mentoring/v1/entity/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ value: 'initial_val', label: 'Initial Label', entity_type_id: entityTypeId, type: 'SYSTEM' }) + + expect(entityRes.status).toBe(201) + const entityId = entityRes.body.result.id + + const url = `/mentoring/v1/entity/update/${entityId}` + let req = request(BASE).put(url) + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + req = req + .send({ + value: 'updatedvalNew', + label: 'Updated Label', + status: 'ACTIVE', + entity_type_id: entityTypeId, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(202) + // validate response schema + const schema = schemas['PUT_/mentoring/v1/entity/update/{id}'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/entity/update/999` // Some non-existent ID + const res = await request(BASE).put(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/entity/read/{id}', () => { + test('should return 200', async () => { + // Create entity type + let value = 'readEntityType' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + const entityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: value, + label: 'Read Test', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + expect(entityTypeRes.status).toBe(201) + const entityTypeId = entityTypeRes.body.result.id + + // Create entity + const entityRes = await request(BASE) + .post('/mentoring/v1/entity/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ value: 'read_val', label: 'Read Label', entity_type_id: entityTypeId, type: 'SYSTEM' }) + expect(entityRes.status).toBe(201) + const entityId = entityRes.body.result.id + + const url = `/mentoring/v1/entity/read/${entityId}` + let req = request(BASE).post(url) + //change menteedetails to admin token + req = req.set('x-auth-token', adminDetails.token) + req = req.set('org-id', adminDetails.organizations[0].id.toString()) + + const res = await req + expect(res.status).toBe(200) + // validate response schema + const schema = schemas['POST_/mentoring/v1/entity/read/{id}'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/entity/read/string` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('DELETE /mentoring/v1/entity/delete/{id}', () => { + test('should return 202', async () => { + let value = 'deleteEntityType' + Math.random().toString(36).substring(2).replace(/[0-9]/g, '') + // Create entity type + const entityTypeRes = await request(BASE) + .post('/mentoring/v1/entity-type/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ + value: value, + label: 'Delete Test', + data_type: 'STRING', + model_names: ['UserExtension'], + status: 'ACTIVE', + }) + expect(entityTypeRes.status).toBe(201) + const entityTypeId = entityTypeRes.body.result.id + + // Create entity + const entityRes = await request(BASE) + .post('/mentoring/v1/entity/create') + .set('x-auth-token', adminDetails.token) + .set('org-id', adminDetails.organizations[0].id.toString()) + .send({ value: 'delete_val', label: 'Delete Label', entity_type_id: entityTypeId, type: 'SYSTEM' }) + expect(entityRes.status).toBe(201) + const entityId = entityRes.body.result.id + + const url = `/mentoring/v1/entity/delete/${entityId}` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', adminDetails.token) + const res = await req + expect(res.status).toBe(202) + // validate response schema + const schema = schemas['DELETE_/mentoring/v1/entity/delete/{id}'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/entity/delete/999` // Some non-existent ID + const res = await request(BASE).delete(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/entity/schemas/entity.schemas.json b/src/integration-tests-new/entity/schemas/entity.schemas.json new file mode 100644 index 000000000..cba324f77 --- /dev/null +++ b/src/integration-tests-new/entity/schemas/entity.schemas.json @@ -0,0 +1,216 @@ +{ + "POST_/mentoring/v1/entity/create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + }, + "entity_type_id": { + "type": "number" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "PUT_/mentoring/v1/entity/update/{id}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "entity_type_id": { "type": "number" }, + "value": { "type": "string" }, + "label": { "type": "string" }, + "status": { "type": "string" }, + "type": { "type": "string" }, + "created_by": { "type": "string" }, + "updated_by": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "deleted_at": { "type": "null" } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_/mentoring/v1/entity/read/{id}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "entity_type_id": { "type": "number" }, + "value": { "type": "string" }, + "label": { "type": "string" }, + "status": { "type": "string" }, + "type": { "type": "string" }, + "created_by": { "type": "string" }, + "updated_by": { "type": ["string", "null"] }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "deleted_at": { "type": "null" } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_/mentoring/v1/entity/delete/{id}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/feedback/feedback.spec.js b/src/integration-tests-new/feedback/feedback.spec.js new file mode 100644 index 000000000..21dd64a34 --- /dev/null +++ b/src/integration-tests-new/feedback/feedback.spec.js @@ -0,0 +1,70 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/feedback.schemas.json') + +describe('feedback endpoints generated from api-doc.yaml', () => { + describe('GET /mentoring/v1/feedback/forms/{SessionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/feedback/forms/1` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_/mentoring/v1/feedback/forms/{SessionId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/feedback/forms/1` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/feedback/submit/{SessionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/feedback/submit/1` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + feedbacks: [ + { + question_id: '1', + value: '1', + }, + ], + feedback_as: 'mentee', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_/mentoring/v1/feedback/submit/{SessionId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/feedback/submit/1` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/feedback/schemas/feedback.schemas.json b/src/integration-tests-new/feedback/schemas/feedback.schemas.json new file mode 100644 index 000000000..775105ec1 --- /dev/null +++ b/src/integration-tests-new/feedback/schemas/feedback.schemas.json @@ -0,0 +1,90 @@ +{ + "GET_/mentoring/v1/feedback/forms/{SessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "form": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "options": { + "type": "null" + }, + "type": { + "type": "string" + }, + "no_of_stars": { + "type": "number" + }, + "status": { + "type": "string" + }, + "category": { + "type": "null" + }, + "rendering_data": { + "type": "object", + "properties": { + "validators": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + } + } + }, + "disable": { + "type": "boolean" + }, + "visible": { + "type": "boolean" + }, + "class": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": {} + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "label": { + "type": "string" + } + } + } + } + } + }, + "meta": { + "type": "object", + "properties": {} + } + } + }, + "POST_/mentoring/v1/feedback/submit/{SessionId}": {} +} diff --git a/src/integration-tests-new/form/form.specs.js b/src/integration-tests-new/form/form.specs.js new file mode 100644 index 000000000..68b5b9231 --- /dev/null +++ b/src/integration-tests-new/form/form.specs.js @@ -0,0 +1,172 @@ +jest.setTimeout(30000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const commonHelper = require('@commonTests') + +const schemas = require('./schemas/form.schemas.json') + +let adminDetails = null +beforeAll(async () => { + adminDetails = await commonHelper.adminLogin() +}) + +describe('form endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/form/create', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/form/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req + .send({ + type: `session_${Date.now()}`, + sub_type: `createSessions_${Date.now()}`, + data: { + template_name: 'defaultTemplate', + fields: { + controls: [ + { + name: 'title', + label: 'title', + value: '', + class: 'ion-margin', + type: 'text', + position: 'floating', + validators: { + required: true, + min_length: 5, + }, + }, + { + name: 'categories', + label: 'Select categories', + value: '', + class: 'ion-margin', + type: 'chip', + position: '', + disabled: false, + show_select_all: true, + validators: { + required: true, + }, + }, + { + name: 'ages', + label: 'Select age', + value: '', + class: 'ion-margin', + type: 'chip', + position: '', + disabled: false, + show_select_all: true, + validators: { + required: true, + }, + }, + ], + }, + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(201) + expect(res.body).toMatchSchema(schemas['POST_/mentoring/v1/form/create']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/form/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('PUT /mentoring/v1/form/update/{formId}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/form/update/1` + let req = request(BASE).put(url) + req = req.set('x-auth-token', adminDetails.token) + req = req + .send({ + type: 'session', + sub_type: 'createSessionsNew', + data: { + template_name: 'Test', + fields: { + controls: [ + { + name: 'title', + label: 'title', + value: '', + class: 'ion-margin', + type: 'text', + position: 'floating', + validators: { + required: true, + min_length: 5, + }, + }, + { + name: 'categories', + label: 'Select categories', + value: '', + class: 'ion-margin', + type: 'chip', + position: '', + disabled: false, + show_select_all: true, + validators: { + required: true, + }, + }, + { + name: 'ages', + label: 'Select age', + value: '', + class: 'ion-margin', + type: 'chip', + position: '', + disabled: false, + show_select_all: true, + validators: { + required: true, + }, + }, + ], + }, + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(202) + expect(res.body).toMatchSchema(schemas['PUT_/mentoring/v1/form/update/{formId}']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/form/update/1` + const res = await request(BASE).put(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/form/read/{formId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/form/read/1` + let req = request(BASE).post(url) + req = req.set('x-auth-token', adminDetails.token) + req = req + .send({ + type: 'session', + sub_type: 'createSessionsNew', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['POST_/mentoring/v1/form/read/{formId}']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/form/read/1` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/form/schemas/form.schemas.json b/src/integration-tests-new/form/schemas/form.schemas.json new file mode 100644 index 000000000..96239b904 --- /dev/null +++ b/src/integration-tests-new/form/schemas/form.schemas.json @@ -0,0 +1,310 @@ +{ + "POST_/mentoring/v1/form/create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "version": { + "type": "number" + }, + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "sub_type": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "template_name": { + "type": "string" + }, + "fields": { + "type": "object", + "properties": { + "controls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "class": { + "type": "string" + }, + "type": { + "type": "string" + }, + "position": { + "type": "string" + }, + "validators": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + }, + "min_length": { + "type": "number" + } + } + }, + "disabled": { + "type": "boolean" + }, + "show_select_all": { + "type": "boolean" + } + }, + "required": [ + "name", + "label", + "value", + "class", + "type", + "position", + "validators" + ] + } + } + } + } + } + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "organization_id": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "version": { + "type": "number" + }, + "type": { + "type": "string" + } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "PUT_/mentoring/v1/form/update/{formId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + } + } + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "POST_/mentoring/v1/form/read/{formId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "version": { + "type": "number" + }, + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "sub_type": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "template_name": { + "type": "string" + }, + "fields": { + "type": "object", + "properties": { + "controls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "class": { + "type": "string" + }, + "type": { + "type": "string" + }, + "position": { + "type": "string" + }, + "validators": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + }, + "min_length": { + "type": "number" + } + } + }, + "disabled": { + "type": "boolean" + }, + "show_select_all": { + "type": "boolean" + } + }, + "required": [ + "name", + "label", + "value", + "class", + "type", + "position", + "validators" + ] + } + } + } + } + } + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "organization_id": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "version": { + "type": "number" + }, + "type": { + "type": "string" + } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/issues/issues.spec.js b/src/integration-tests-new/issues/issues.spec.js new file mode 100644 index 000000000..c6c42470e --- /dev/null +++ b/src/integration-tests-new/issues/issues.spec.js @@ -0,0 +1,38 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/issues.schemas.json') + +describe('issues endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/issues/create', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/issues/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + descriptaion: 'string', + meta_data: { + request_type: 'string', + browserName: 'string', + browserVersion: 'string', + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_issues_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/issues/schemas/issues.schemas.json b/src/integration-tests-new/issues/schemas/issues.schemas.json new file mode 100644 index 000000000..2efb7f085 --- /dev/null +++ b/src/integration-tests-new/issues/schemas/issues.schemas.json @@ -0,0 +1,37 @@ +{ + "POST_mentoring_v1_issues_create": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/mentees/mentees.specs.js b/src/integration-tests-new/mentees/mentees.specs.js new file mode 100644 index 000000000..98b9a58f8 --- /dev/null +++ b/src/integration-tests-new/mentees/mentees.specs.js @@ -0,0 +1,119 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +const schemas = require('./schemas/mentees.schemas.json') +let userDetails = null + +beforeAll(async () => { + console.log('setting up global variables....') + userDetails = await commonHelper.logIn() + + let profileCreate = await request(BASE) + .post('/mentoring/v1/profile/create') + .set('x-auth-token', userDetails.token) + .send({ + designation: ['beo', 'deo', 'testt'], + area_of_expertise: ['educational_leadership', 'sqaa'], + education_qualification: 'MBA', + tags: ['Experienced', 'Technical'], + visibility: 'visible', + organisation_ids: [1], + external_session_visibility: 'CURRENT', + external_mentor_visibility: 'ALL', + }) +}) + +describe('mentees endpoints generated from api-doc.yaml', () => { + // describe('GET /mentoring/v1/mentees/sessions', () => { + // test('should return 200', async () => { + // const url = `/mentoring/v1/mentees/sessions?page=1&limit=2`; + // let req = request(BASE).get(url); + // req = req.set('x-auth-token', "string"); + // const res = await req; + // expect(res.status).toBeGreaterThanOrEqual(200); + // expect(res.status).toBeLessThan(300); + // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/sessions']); + // }); + + // test('should return 401/403 when unauthorized', async () => { + // const url = `/mentoring/v1/mentees/sessions?page=1&limit=2`; + // const res = await request(BASE).get(url); + // expect([401,403]).toContain(res.status); + // }); + + // }); + + // describe('GET /mentoring/v1/mentees/joinSession/{sessionId}', () => { + // test('should return 200', async () => { + // const url = `/mentoring/v1/mentees/joinSession/62832531a05cbd57b273aebb`; + // let req = request(BASE).get(url); + // req = req.set('x-auth-token', "string"); + // const res = await req; + // expect(res.status).toBeGreaterThanOrEqual(200); + // expect(res.status).toBeLessThan(300); + // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/joinSession/{sessionId}']); + // }); + + // test('should return 401/403 when unauthorized', async () => { + // const url = `/mentoring/v1/mentees/joinSession/62832531a05cbd57b273aebb`; + // const res = await request(BASE).get(url); + // expect([401,403]).toContain(res.status); + // }); + + // }); + + // describe('GET /mentoring/v1/mentees/reports', () => { + // test('should return 200', async () => { + // const url = `/mentoring/v1/mentees/reports?filterType=QUARTERLY`; + // let req = request(BASE).get(url); + // req = req.set('x-auth-token', "string"); + // const res = await req; + // expect(res.status).toBeGreaterThanOrEqual(200); + // expect(res.status).toBeLessThan(300); + // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/reports']); + // }); + + // test('should return 401/403 when unauthorized', async () => { + // const url = `/mentoring/v1/mentees/reports?filterType=QUARTERLY`; + // const res = await request(BASE).get(url); + // expect([401,403]).toContain(res.status); + // }); + + // }); + + describe('GET /mentoring/v1/mentees/homeFeed', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentees/homeFeed` + let req = request(BASE).get(url) + req = req.set('x-auth-token', userDetails.token) + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/homeFeed']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentees/homeFeed` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/mentees/list', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentees/list?page=1&limit=5&search=&connected_mentees=true&mentorId=${userDetails.id}` + let req = request(BASE).get(url) + req = req.set('x-auth-token', userDetails.token) + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/list']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentees/list?page=1&limit=5&search=&connected_mentees=true&mentorId=${userDetails.id}` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/mentees/schemas/mentees.schemas.json b/src/integration-tests-new/mentees/schemas/mentees.schemas.json new file mode 100644 index 000000000..ad4075c89 --- /dev/null +++ b/src/integration-tests-new/mentees/schemas/mentees.schemas.json @@ -0,0 +1,247 @@ +{ + "GET_/mentoring/v1/mentees/sessions": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "meeting_info": { + "type": "object", + "properties": { + "platform": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "created_at": { + "type": "string" + }, + "is_enrolled": { + "type": "boolean" + }, + "mentor_name": { + "type": "string" + }, + "organization_id": { + "type": "string" + } + }, + "required": [ + "id", + "title", + "description", + "image", + "user_id", + "status", + "start_date", + "end_date", + "meeting_info", + "created_at", + "is_enrolled", + "mentor_name", + "organization_id" + ] + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentees/joinSession/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "link": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentees/reports": { + "type": "object", + "properties": { + "response_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "total_session_enrolled": { + "type": "number" + }, + "total_sessions_attended": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": {} + } + } + }, + "GET_/mentoring/v1/mentees/homeFeed": { + "type": "object", + "properties": { + "responseCode": { "type": "string" }, + "message": { "type": "string" }, + "result": { + "type": "object", + "properties": { + "my_sessions": { + "type": "array" + }, + "my_sessions_count": { + "type": ["number", "integer"] + } + } + }, + "meta": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "data": { "type": "array" }, + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { "type": "string" }, + "meetingPlatform": { "type": "string" } + }, + "required": ["type", "data", "correlation", "meetingPlatform"] + } + }, + "required": ["responseCode", "message", "result", "meta"], + "additionalProperties": true + }, + "GET_/mentoring/v1/mentees/list": { + "type": "object", + + "properties": { + "message": { "type": "string" }, + + "result": { + "type": "object", + "properties": { + "data": { + "type": "array" + }, + "count": { + "type": ["number", "integer"] + } + }, + "required": ["data", "count"], + "additionalProperties": true + }, + + "meta": {} + }, + + "required": ["message", "result"], + + "additionalProperties": true + } +} diff --git a/src/integration-tests-new/mentoring/mentoring.spec.js b/src/integration-tests-new/mentoring/mentoring.spec.js new file mode 100644 index 000000000..762d20e45 --- /dev/null +++ b/src/integration-tests-new/mentoring/mentoring.spec.js @@ -0,0 +1,27 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/mentoring.schemas.json') + +describe('mentoring endpoints generated from api-doc.yaml', () => { + describe('GET /mentoring/health', () => { + test('should return 200', async () => { + const url = `/mentoring/health` + let req = request(BASE).get(url) + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_health'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/mentoring/schemas/mentoring.schemas.json b/src/integration-tests-new/mentoring/schemas/mentoring.schemas.json new file mode 100644 index 000000000..ac7ccaba4 --- /dev/null +++ b/src/integration-tests-new/mentoring/schemas/mentoring.schemas.json @@ -0,0 +1,71 @@ +{ + "GET_mentoring_health": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "ver": { + "type": "string" + }, + "ts": { + "type": "string" + }, + "params": { + "type": "object", + "properties": { + "resmsgid": { + "type": "string" + }, + "msgid": { + "type": "string" + }, + "status": { + "type": "string" + }, + "err": { + "type": "null" + }, + "errMsg": { + "type": "null" + } + } + }, + "status": { + "type": "number" + }, + "result": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "healthy": { + "type": "boolean" + }, + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "healthy": { + "type": "boolean" + }, + "err": { + "type": "string" + }, + "errMsg": { + "type": "string" + } + }, + "required": ["name", "healthy", "err", "errMsg"] + } + } + } + } + } + } +} diff --git a/src/integration-tests-new/mentors/mentors.specs.js b/src/integration-tests-new/mentors/mentors.specs.js new file mode 100644 index 000000000..f7ea210a1 --- /dev/null +++ b/src/integration-tests-new/mentors/mentors.specs.js @@ -0,0 +1,143 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let userDetails = null +const schemas = require('./schemas/mentors.schemas.json') + +beforeAll(async () => { + console.log('setting up global variables....') + userDetails = await commonHelper.mentorLogIn() + + /* + let profileCreate = await request(BASE).post('/mentoring/v1/profile/create').set('x-auth-token', userDetails.token).send({ + designation: ['beo', 'deo', 'testt'], + area_of_expertise: ['educational_leadership', 'sqaa'], + education_qualification: 'MBA', + tags: ['Experienced', 'Technical'], + visibility: 'visible', + organisation_ids: [1], + external_session_visibility: 'CURRENT', + external_mentor_visibility: 'ALL', + }) + + console.log(profileCreate.body, 'profileCreatebody') + */ +}) + +describe('mentors endpoints generated from api-doc.yaml', () => { + /* + describe('GET /mentoring/v1/mentors/details/{mentorId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/details/62a820225ff93f30cfe5f990`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "string"); + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/details/{mentorId}']) + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/details/62a820225ff93f30cfe5f990`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('GET /mentoring/v1/mentors/reports', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/reports?filterType=QUARTERLY`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "string"); + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/reports']) + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/reports?filterType=QUARTERLY`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('GET /mentoring/v1/mentors/upcomingSessions/{mentorId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/upcomingSessions/62a820225ff93f30cfe5f990?page=1&limit=2&search=jhon`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "string"); + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/upcomingSessions/{mentorId}']) + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/upcomingSessions/62a820225ff93f30cfe5f990?page=1&limit=2&search=jhon`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('GET /mentoring/v1/mentors/share/{mentorId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/share/21`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "string"); + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/share/{mentorId}']) + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/share/21`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + */ + + describe('GET /mentoring/v1/mentors/list', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/list?page=1&limit=10&search=&directory=true&search_on=` + let req = request(BASE).get(url) + req = req.set('x-auth-token', userDetails.token) + const res = await req + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/list']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/list?page=1&limit=100&search=&directory=true&search_on=` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/mentors/createdSessions', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/mentors/createdSessions?page=1&limit=100&search=` + let req = request(BASE).get(url) + req = req.set('x-auth-token', userDetails.token) + const res = await req + + expect(res.status).toBe(200) + expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentors/createdSessions']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/mentors/createdSessions?page=1&limit=100&search=` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/mentors/schemas/mentors.schemas.json b/src/integration-tests-new/mentors/schemas/mentors.schemas.json new file mode 100644 index 000000000..9ac8e46b3 --- /dev/null +++ b/src/integration-tests-new/mentors/schemas/mentors.schemas.json @@ -0,0 +1,381 @@ +{ + "GET_/mentoring/v1/mentors/details/{mentorId}": { + "type": "object", + "properties": { + "response_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "sessions_attended": { + "type": "number" + }, + "sessions_hosted": { + "type": "number" + }, + "id": { + "type": "number" + }, + "email": { + "type": "string" + }, + "verified": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "terms_and_conditions": { + "type": "boolean" + }, + "designation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "location": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "languages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "last_logged_in_at": { + "type": "string" + }, + "about": { + "type": "string" + }, + "experience": { + "type": "string" + }, + "user_roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "user_type": { + "type": "number" + }, + "status": { + "type": "string" + } + } + } + }, + "image": { + "type": "string" + }, + "rating": { + "type": "object", + "properties": { + "average": { + "type": "number" + }, + "votes": { + "type": "number" + }, + "breakup": { + "type": "array", + "items": { + "type": "object", + "properties": { + "star": { + "type": "number" + }, + "votes": { + "type": "number" + } + }, + "required": ["star", "votes"] + } + } + } + }, + "preferred_language": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentors/reports": { + "type": "object", + "properties": { + "response_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "total_session_created": { + "type": "number" + }, + "total_session_hosted": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentors/upcomingSessions/{mentorId}": { + "type": "object", + "properties": { + "response_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "meeting_info": { + "type": "object", + "properties": { + "platform": { + "type": "string" + } + } + }, + "mentor_name": { + "type": "string" + }, + "organization_id": { + "type": "string" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentors/share/{mentorId}": { + "type": "object", + "properties": { + "response_code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "share_link": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/mentors/list": { + "type": "object", + "properties": { + "message": { "type": "string" }, + + "result": { + "type": "object", + "properties": { + "data": { + "type": "array" + }, + "count": { + "type": ["number", "integer"] + } + }, + "required": ["data", "count"], + "additionalProperties": true + }, + + "meta": {} + }, + + "required": ["message", "result"], + + "additionalProperties": true + }, + "GET_/mentoring/v1/mentors/createdSessions": { + "type": "object", + + "properties": { + "message": { "type": "string" }, + + "result": { + "oneOf": [ + { + "type": "object", + "properties": { + "count": { "type": ["number", "integer"] }, + "data": { "type": "array" } + }, + "required": ["count", "data"], + "additionalProperties": true + }, + { + "type": "array" + } + ] + }, + + "meta": {} + }, + + "required": ["message", "result"], + + "additionalProperties": true + } +} diff --git a/src/integration-tests-new/modules/modules.spec.js b/src/integration-tests-new/modules/modules.spec.js new file mode 100644 index 000000000..f92690614 --- /dev/null +++ b/src/integration-tests-new/modules/modules.spec.js @@ -0,0 +1,113 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/modules.schemas.json') + +describe('modules endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/modules/create', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/modules/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + code: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_modules_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/modules/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/modules/update/{id}', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/modules/update/{id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + code: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_modules_update_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/modules/update/{id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('DELETE /mentoring/v1/modules/delete/{id}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/modules/delete/{id}` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_modules_delete_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/modules/list?page={page}&limit={limit}&search={search}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/modules/list?page=1&limit=2&search=John` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_modules_list_page_page_limit_limit_search_search'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/modules/schemas/modules.schemas.json b/src/integration-tests-new/modules/schemas/modules.schemas.json new file mode 100644 index 000000000..3c2db8bc2 --- /dev/null +++ b/src/integration-tests-new/modules/schemas/modules.schemas.json @@ -0,0 +1,152 @@ +{ + "POST_mentoring_v1_modules_create": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "Id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_modules_update_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "status": { + "type": "string" + }, + "code": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_modules_delete_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": {} + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_modules_list_page_page_limit_limit_search_search": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": ["id", "code", "status"] + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/org-admin/org-admin.spec.js b/src/integration-tests-new/org-admin/org-admin.spec.js new file mode 100644 index 000000000..4d64118d9 --- /dev/null +++ b/src/integration-tests-new/org-admin/org-admin.spec.js @@ -0,0 +1,203 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/org-admin.schemas.json') + +describe('org-admin endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/org-admin/roleChange', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/org-admin/roleChange` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + user_id: 'string', + current_roles: ['string'], + new_roles: ['string'], + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_roleChange'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/org-admin/inheritEntityType', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/inheritEntityType` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + entity_type_value: 'string', + target_entity_type_label: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_inheritEntityType'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/org-admin/getOrgPolicies', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/getOrgPolicies` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_org-admin_getOrgPolicies'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/org-admin/updateRelatedOrgs', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/updateRelatedOrgs` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + responseCode: 'string', + message: 'string', + result: ['string'], + meta: { + correlation: 'string', + meetingPlatform: 'string', + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_updateRelatedOrgs'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/org-admin/setOrgPolicies', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/setOrgPolicies` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + session_visibility_policy: 'string', + mentor_visibility_policy: 'string', + external_session_visibility_policy: 'string', + external_mentor_visibility_policy: 'string', + allow_mentor_override: true, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_setOrgPolicies'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/org-admin/uploadSampleCSV', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/uploadSampleCSV` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send('string').set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_uploadSampleCSV'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/org-admin/updateTheme', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/updateTheme` + let req = request(BASE).post(url) + req = req.set('X-auth-token', 'bearer {{token}}') + req = req + .send({ + primaryColor: '#E74C3C', + secondaryColor: '#F1C40F', + backgroundColor: '#FFFFFF', + textColor: '#34495E', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_org-admin_updateTheme'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/org-admin/themeDetails', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/org-admin/themeDetails` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_org-admin_themeDetails'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/org-admin/schemas/org-admin.schemas.json b/src/integration-tests-new/org-admin/schemas/org-admin.schemas.json new file mode 100644 index 000000000..d87b9eb6b --- /dev/null +++ b/src/integration-tests-new/org-admin/schemas/org-admin.schemas.json @@ -0,0 +1,396 @@ +{ + "POST_mentoring_v1_org-admin_roleChange": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "user_id": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_org-admin_inheritEntityType": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "allow_filtering": { + "type": "boolean" + }, + "data_type": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "parent_id": { + "type": "number" + }, + "allow_custom_entities": { + "type": "boolean" + }, + "has_entities": { + "type": "boolean" + }, + "model_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_org-admin_getOrgPolicies": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "organization_id": { + "type": "string" + }, + "session_visibility_policy": { + "type": "string" + }, + "mentor_visibility_policy": { + "type": "string" + }, + "external_session_visibility_policy": { + "type": "string" + }, + "external_mentor_visibility_policy": { + "type": "string" + }, + "approval_required_for": { + "type": "array", + "items": { + "type": "string" + } + }, + "allow_mentor_override": { + "type": "boolean" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_org-admin_updateRelatedOrgs": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "organization_id": { + "type": "string" + }, + "session_visibility_policy": { + "type": "string" + }, + "mentor_visibility_policy": { + "type": "string" + }, + "external_session_visibility_policy": { + "type": "string" + }, + "external_mentor_visibility_policy": { + "type": "string" + }, + "approval_required_for": { + "type": "array", + "items": { + "type": "string" + } + }, + "allow_mentor_override": { + "type": "boolean" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_org-admin_setOrgPolicies": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "session_visibility_policy": { + "type": "string" + }, + "mentor_visibility_policy": { + "type": "string" + }, + "external_session_visibility_policy": { + "type": "string" + }, + "external_mentor_visibility_policy": { + "type": "string" + }, + "allow_mentor_override": { + "type": "boolean" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "approval_required_for": { + "type": "array", + "items": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_org-admin_uploadSampleCSV": { + "responseCode": "OK", + "message": "CSV_UPLOADED_SUCCESSFULLY", + "result": [], + "meta": { + "formsVersion": [ + { + "id": 3, + "type": "termsAndConditions", + "version": 0 + }, + { + "id": 4, + "type": "faq", + "version": 0 + }, + { + "id": 5, + "type": "helpVideos", + "version": 0 + }, + { + "id": 6, + "type": "platformApp", + "version": 4 + }, + { + "id": 1, + "type": "editProfile", + "version": 0 + }, + { + "id": 2, + "type": "session", + "version": 0 + }, + { + "id": 8, + "type": "sampleCsvDownload", + "version": 0 + }, + { + "id": 7, + "type": "helpApp", + "version": 0 + }, + { + "id": 9, + "type": "mentorQuestionnaire", + "version": 0 + }, + { + "id": 10, + "type": "managersSession", + "version": 0 + } + ], + "correlation": "8d1d9ec0-68c4-4def-9dad-2cac06ed71a2", + "meetingPlatform": "BBB" + }, + "type": "string" + }, + "POST_mentoring_v1_org-admin_updateTheme": { + "type": "string" + }, + "GET_mentoring_v1_org-admin_themeDetails": { + "type": "string" + } +} diff --git a/src/integration-tests-new/permissions/permissions.spec.js b/src/integration-tests-new/permissions/permissions.spec.js new file mode 100644 index 000000000..783852a34 --- /dev/null +++ b/src/integration-tests-new/permissions/permissions.spec.js @@ -0,0 +1,112 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/permissions.schemas.json') + +describe('permissions endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/permissions/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/permissions/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + code: 'string', + module: 'string', + request_type: ['string'], + api_path: 'string', + status: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_permissions_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/permissions/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/permissions/update/{id}', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/permissions/update/{id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + code: 'string', + module: 'string', + request_type: ['string'], + api_path: 'string', + status: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_permissions_update_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('DELETE /mentoring/v1/permissions/delete/{id}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/permissions/delete/{id}` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_permissions_delete_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/permissions/list?page={page}&limit={limit}&search={search}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/permissions/list?page=1&limit=2&search=John` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_permissions_list_page_page_limit_limit_search_search'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/permissions/schemas/permissions.schemas.json b/src/integration-tests-new/permissions/schemas/permissions.schemas.json new file mode 100644 index 000000000..06635c97a --- /dev/null +++ b/src/integration-tests-new/permissions/schemas/permissions.schemas.json @@ -0,0 +1,180 @@ +{ + "POST_mentoring_v1_permissions_create": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "Id": { + "type": "number" + }, + "status": { + "type": "string" + }, + "module": { + "type": "string" + }, + "request_type": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_permissions_update_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "Id": { + "type": "number" + }, + "status": { + "type": "string" + }, + "module": { + "type": "string" + }, + "request_type": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_permissions_delete_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": {} + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_permissions_list_page_page_limit_limit_search_search": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "results": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "module": { + "type": "string" + }, + "request_type": { + "type": "array", + "items": { + "type": "string" + } + }, + "api_path": { + "type": "string" + }, + "status": { + "type": "string" + } + } + } + }, + "count": { + "type": "number" + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/profile/profile.specs.js b/src/integration-tests-new/profile/profile.specs.js new file mode 100644 index 000000000..d8ddfc159 --- /dev/null +++ b/src/integration-tests-new/profile/profile.specs.js @@ -0,0 +1,62 @@ +jest.setTimeout(100000) +const request = require('supertest') +const fs = require('fs') +const path = require('path') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let mentorDetails = null +const schemas = require('./schemas/profile.schemas.json') + +beforeAll(async () => { + mentorDetails = await commonHelper.logIn() +}) + +describe('profile endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/profile/update', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/profile/update` + let req = request(BASE).post(url) + req = req.set('x-auth-token', mentorDetails.token) + req = req + .send({ + designation: ['Principal'], + area_of_expertise: ['educational_leadership'], + experience: '5', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBe(200) + // validate response schema + const schema = schemas['POST_mentoring_v1_profile_update'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('GET /mentoring/v1/profile/filterList?entity_types={entity_types}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/profile/filterList?entity_types=designation` + let req = request(BASE).get(url) + req = req.set('x-auth-token', mentorDetails.token) + const res = await req + expect(res.status).toBe(200) + // validate response schema + const schema = schemas['GET_mentoring_v1_profile_filterList_entity_types_entity_types'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('GET /mentoring/v1/profile/details', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/profile/details/${mentorDetails.userId}` + let req = request(BASE).get(url) + req = req.set('x-auth-token', mentorDetails.token) + const res = await req + expect(res.status).toBe(200) + + // validate response schema + const schema = schemas['GET_mentoring_v1_profile_details'] + expect(res.body).toMatchSchema(schema) + }) + }) +}) diff --git a/src/integration-tests-new/profile/schemas/profile.schemas.json b/src/integration-tests-new/profile/schemas/profile.schemas.json new file mode 100644 index 000000000..0294890e8 --- /dev/null +++ b/src/integration-tests-new/profile/schemas/profile.schemas.json @@ -0,0 +1,458 @@ +{ + "POST_mentoring_v1_profile_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "designation": { + "type": "string" + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "rating": { + "type": "object", + "properties": { + "average": { + "type": "number" + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "age": { + "type": "number" + }, + "experience": { + "type": "string" + } + } + }, + "stats": { + "type": "object", + "properties": { + "sessions_attended": { + "type": "number" + }, + "students_mentored": { + "type": "number" + } + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "configs": { + "type": "object", + "properties": { + "notification": { + "type": "boolean" + }, + "visibility": { + "type": "string" + } + } + }, + "visibility": { + "type": "string" + }, + "organisation_ids": { + "type": "array", + "items": { + "type": "number" + } + }, + "external_session_visibility": { + "type": "string" + }, + "external_mentor_visibility": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "user_roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "user_type": { + "type": "number" + }, + "status": { + "type": "string" + } + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_profile_update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "user_id": { + "type": "string" + }, + "designation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "label": { "type": "string" } + } + } + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "label": { "type": "string" } + } + } + }, + "education_qualification": { "type": "null" }, + "rating": { "type": "null" }, + "meta": { "type": "null" }, + "stats": { "type": "null" }, + "tags": { "type": "null" }, + "configs": { "type": "null" }, + "visible_to_organizations": { + "type": "array", + "items": { "type": "string" } + }, + "external_session_visibility": { "type": "string" }, + "experience": { "type": "string" }, + "organization_id": { "type": "string" }, + "external_mentee_visibility": { "type": "string" }, + "mentee_visibility": { "type": "string" }, + "external_mentor_visibility": { "type": "string" }, + "mentor_visibility": { "type": "string" }, + "name": { "type": "string" }, + "email": { "type": "string" }, + "phone": { "type": "null" }, + "is_mentor": { "type": "boolean" }, + "settings": { + "type": "object", + "properties": { + "chat_enabled": { "type": "boolean" } + } + }, + "image": { "type": "string" }, + "gender": { "type": "null" }, + "status": { "type": "string" }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_profile_filterList_entity_types_entity_types": {}, + "GET_mentoring_v1_profile_details": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "user_id": { + "type": "string" + }, + "designation": { + "type": ["array", "null"], + "items": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "label": { "type": "string" } + } + } + }, + "area_of_expertise": { + "type": ["array", "null"], + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { "type": ["null", "string", "array"] }, + "rating": { "type": ["null", "object"] }, + "meta": { "type": ["null", "object"] }, + "stats": { "type": ["null", "object"] }, + "tags": { "type": ["null", "array"] }, + "configs": { "type": ["null", "object"] }, + "external_session_visibility": { + "type": "string" + }, + "experience": { + "type": ["null", "string"] + }, + "organization_id": { + "type": "string" + }, + "external_mentee_visibility": { + "type": "string" + }, + "mentee_visibility": { + "type": "string" + }, + "external_mentor_visibility": { + "type": "string" + }, + "mentor_visibility": { + "type": "string" + }, + "name": { + "type": "string" + }, + "is_mentor": { + "type": "boolean" + }, + "settings": { + "type": "object", + "properties": { + "chat_enabled": { "type": "boolean" } + } + }, + "image": { "type": "string" }, + "gender": { + "type": "null" + }, + "status": { + "type": "string" + }, + "organization_code": { + "type": "string" + }, + "tenant_code": { + "type": "string" + }, + "user_name": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "profile_mandatory_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "organization": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": ["null", "string"] } + } + }, + "sessions_attended": { + "type": "number" + }, + "sessions_hosted": { + "type": "number" + }, + "is_connected": { + "type": "boolean" + }, + "visible_to_organizations": { + "type": "array", + "items": { "type": "string" } + }, + "displayProperties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { "type": "string" }, + "label": { "type": "string" }, + "visible": { "type": "boolean" }, + "visibility": { "type": "string" }, + "sequence": { "type": "number" } + }, + "required": ["key"] + } + }, + "Permissions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { "type": "string" }, + "request_type": { + "type": "array", + "items": { "type": "string" } + }, + "service": { "type": "string" } + }, + "required": ["module", "request_type", "service"] + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/question-set/question-set.spec.js b/src/integration-tests-new/question-set/question-set.spec.js new file mode 100644 index 000000000..a115154a9 --- /dev/null +++ b/src/integration-tests-new/question-set/question-set.spec.js @@ -0,0 +1,96 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/question-set.schemas.json') + +describe('question-set endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/question-set/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/question-set/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + questions: [1], + code: 'feedback', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_/mentoring/v1/question-set/create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/question-set/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('PATCH /mentoring/v1/question-set/update/{QuestionSetId}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/question-set/update/1` + let req = request(BASE).patch(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + questions: [1], + code: 'UpdatedFeedbackCode', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['PATCH_/mentoring/v1/question-set/update/{QuestionSetId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/question-set/update/1` + const res = await request(BASE).patch(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/question-set/read/{QuestionSetId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/question-set/read/1` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_/mentoring/v1/question-set/read/{QuestionSetId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/question-set/read/1` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/question-set/schemas/question-set.schemas.json b/src/integration-tests-new/question-set/schemas/question-set.schemas.json new file mode 100644 index 000000000..d60b9afe5 --- /dev/null +++ b/src/integration-tests-new/question-set/schemas/question-set.schemas.json @@ -0,0 +1,176 @@ +{ + "POST_/mentoring/v1/question-set/create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "id": { + "type": "number" + }, + "questions": { + "type": "array", + "items": { + "type": "string" + } + }, + "code": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "meta": { + "type": "null" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "PATCH_/mentoring/v1/question-set/update/{QuestionSetId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "id": { + "type": "number" + }, + "questions": { + "type": "array", + "items": { + "type": "string" + } + }, + "code": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "meta": { + "type": "null" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "POST_/mentoring/v1/question-set/read/{QuestionSetId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "questions": { + "type": "array", + "items": { + "type": "string" + } + }, + "code": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/questions/questions.spec.js b/src/integration-tests-new/questions/questions.spec.js new file mode 100644 index 000000000..527e000e7 --- /dev/null +++ b/src/integration-tests-new/questions/questions.spec.js @@ -0,0 +1,124 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/questions.schemas.json') + +describe('questions endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/questions/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/questions/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + name: 'keyvalue', + question: 'To what extent did you feel comfortable sharing your thoughts in the session?', + type: 'rating', + options: null, + no_of_stars: 5, + status: 'active', + category: null, + rendering_data: { + value: '', + class: 'ion-margin', + disabled: false, + noOfstars: '5', + position: 'floating', + validation: { + required: false, + }, + }, + meta: null, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_/mentoring/v1/questions/create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/questions/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('PUT /mentoring/v1/questions/update/{QuestionId}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/questions/update/1` + let req = request(BASE).put(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + name: 'keyvalue', + question: 'To what extent were you able to learn new skills in the session Org?', + type: 'rating', + options: null, + no_of_stars: 5, + status: 'active', + category: { + evaluating: 'mentor', + }, + rendering_data: { + validators: { + required: true, + }, + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['PUT_/mentoring/v1/questions/update/{QuestionId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/questions/update/1` + const res = await request(BASE).put(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/questions/read/{QuestionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/questions/read/1` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_/mentoring/v1/questions/read/{QuestionId}'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/questions/read/1` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/questions/schemas/questions.schemas.json b/src/integration-tests-new/questions/schemas/questions.schemas.json new file mode 100644 index 000000000..18ea61450 --- /dev/null +++ b/src/integration-tests-new/questions/schemas/questions.schemas.json @@ -0,0 +1,293 @@ +{ + "POST_/mentoring/v1/questions/create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "question": { + "type": "string" + }, + "type": { + "type": "string" + }, + "options": { + "type": "null" + }, + "no_of_stars": { + "type": "number" + }, + "status": { + "type": "string" + }, + "category": { + "type": "null" + }, + "rendering_data": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "class": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "noOfstars": { + "type": "string" + }, + "position": { + "type": "string" + }, + "validation": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + } + } + } + } + }, + "meta": { + "type": "null" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "PUT_/mentoring/v1/questions/update/{QuestionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "question": { + "type": "string" + }, + "type": { + "type": "string" + }, + "options": { + "type": "null" + }, + "no_of_stars": { + "type": "number" + }, + "status": { + "type": "string" + }, + "category": { + "type": "null" + }, + "rendering_data": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "class": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "noOfstars": { + "type": "string" + }, + "position": { + "type": "string" + }, + "validation": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + } + } + } + } + }, + "meta": { + "type": "null" + }, + "updated_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/questions/read/{QuestionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "question": { + "type": "string" + }, + "options": { + "type": "null" + }, + "type": { + "type": "string" + }, + "no_of_stars": { + "type": "number" + }, + "status": { + "type": "string" + }, + "category": { + "type": "null" + }, + "rendering_data": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "class": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "noOfstars": { + "type": "string" + }, + "position": { + "type": "string" + }, + "validation": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + } + } + } + } + }, + "meta": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "string" + } + }, + "correlation": { + "type": "string" + }, + "meeting_platform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/report-mapping/report-mapping.spec.js b/src/integration-tests-new/report-mapping/report-mapping.spec.js new file mode 100644 index 000000000..89a0cad46 --- /dev/null +++ b/src/integration-tests-new/report-mapping/report-mapping.spec.js @@ -0,0 +1,106 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/report-mapping.schemas.json') + +describe('report-mapping endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/report-mapping/create', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-mapping/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + report_code: 'string', + role_title: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-mapping_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/report-mapping/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/report-mapping/read', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-mapping/read?code=total_number_of_sessions_attended` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_report-mapping_read'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/report-mapping/update', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-mapping/update?id=16` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + report_code: 'string', + role_title: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-mapping_update'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('DELETE /mentoring/v1/report-mapping/delete', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-mapping/delete?id=16` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_report-mapping_delete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/report-mapping/schemas/report-mapping.schemas.json b/src/integration-tests-new/report-mapping/schemas/report-mapping.schemas.json new file mode 100644 index 000000000..1cf422852 --- /dev/null +++ b/src/integration-tests-new/report-mapping/schemas/report-mapping.schemas.json @@ -0,0 +1,237 @@ +{ + "POST_mentoring_v1_report-mapping_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "report_code": { + "type": "string" + }, + "role_title": { + "type": "string" + }, + "id": { + "type": "number" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_report-mapping_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "report_code": { + "type": "string" + }, + "role_title": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_report-mapping_update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "report_code": { + "type": "string" + }, + "role_title": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_report-mapping_delete": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/report-queries/report-queries.spec.js b/src/integration-tests-new/report-queries/report-queries.spec.js new file mode 100644 index 000000000..472eae998 --- /dev/null +++ b/src/integration-tests-new/report-queries/report-queries.spec.js @@ -0,0 +1,94 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/report-queries.schemas.json') + +describe('report-queries endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/report-queries/create', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-queries/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-queries_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/report-queries/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/report-queries/read', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-queries/read?code=string` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_report-queries_read'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/report-queries/update', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/report-queries/update?code=string` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-queries_update'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('DELETE /mentoring/v1/report-queries/delete', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-queries/delete?id=1` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_report-queries_delete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/report-queries/schemas/report-queries.schemas.json b/src/integration-tests-new/report-queries/schemas/report-queries.schemas.json new file mode 100644 index 000000000..8af5a847a --- /dev/null +++ b/src/integration-tests-new/report-queries/schemas/report-queries.schemas.json @@ -0,0 +1,246 @@ +{ + "POST_mentoring_v1_report-queries_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "status": { + "type": "string" + }, + "report_code": { + "type": "string" + }, + "query": { + "type": "string" + }, + "id": { + "type": "number" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_report-queries_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "report_code": { + "type": "string" + }, + "query": { + "type": "string" + }, + "status": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_report-queries_update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "report_code": { + "type": "string" + }, + "query": { + "type": "string" + }, + "status": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_report-queries_delete": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/report-type/report-type.spec.js b/src/integration-tests-new/report-type/report-type.spec.js new file mode 100644 index 000000000..02d05b1d0 --- /dev/null +++ b/src/integration-tests-new/report-type/report-type.spec.js @@ -0,0 +1,104 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/report-type.schemas.json') + +describe('report-type endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/report-type/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/report-type/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + title: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-type_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/report-type/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/report-type/read', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-type/read?title=random_title` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_report-type_read'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/report-type/update', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-type/update?id=1` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + title: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_report-type_update'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('DELETE /mentoring/v1/report-type/delete', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/report-type/delete?id=1` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_report-type_delete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/report-type/schemas/report-type.schemas.json b/src/integration-tests-new/report-type/schemas/report-type.schemas.json new file mode 100644 index 000000000..a274988d3 --- /dev/null +++ b/src/integration-tests-new/report-type/schemas/report-type.schemas.json @@ -0,0 +1,228 @@ +{ + "POST_mentoring_v1_report-type_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "title": { + "type": "string" + }, + "id": { + "type": "number" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_report-type_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_report-type_update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_report-type_delete": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/reports/reports.spec.js b/src/integration-tests-new/reports/reports.spec.js new file mode 100644 index 000000000..c6f0acb02 --- /dev/null +++ b/src/integration-tests-new/reports/reports.spec.js @@ -0,0 +1,184 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/reports.schemas.json') + +describe('reports endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/reports/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/reports/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + code: 'string', + title: 'string', + description: 'string', + report_type_title: 'string', + created_at: 'string', + updated_at: 'string', + config: { + columns: [ + { + key: 'string', + label: 'string', + filter: true, + sort: true, + search: true, + filterType: 'string', + isEntityType: true, + isMultipleFilter: true, + dataType: 'string', + defaultValues: [ + { + label: 'string', + value: 'string', + }, + ], + }, + ], + }, + organization_id: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_reports_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/reports/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/reports/read', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/reports/read?id=1` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_reports_read'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/reports/update/{id}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/reports/update/string` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + code: 'string', + title: 'string', + description: 'string', + report_type_title: 'string', + config: 'string', + organization_id: 1, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_reports_update_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('DELETE /mentoring/v1/reports/delete', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/reports/delete?id=1` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_reports_delete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/reports/reportData', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/reports/reportData?report_code=report_code&report_role=role&start_date=1735756200&end_date=1738348199&session_type=All&download_csv=string&pageNo=1&Limit=1&organization=string&group_by=string&entities_value=string&sort_column=string&sort_value=string` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + filters: { + mentor_name: ['string'], + }, + search: { + mentor_name: ['string'], + }, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_reports_reportData'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/reports/filterList', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/reports/filterList?filter_type=session&report_filter=true` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_reports_filterList'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integration-tests-new/reports/schemas/reports.schemas.json b/src/integration-tests-new/reports/schemas/reports.schemas.json new file mode 100644 index 000000000..7e7d7c57b --- /dev/null +++ b/src/integration-tests-new/reports/schemas/reports.schemas.json @@ -0,0 +1,838 @@ +{ + "POST_mentoring_v1_reports_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "report_type_title": { + "type": "string" + }, + "config": { + "type": "object", + "properties": { + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "sort": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "filter": { + "type": "boolean" + }, + "search": { + "type": "boolean" + }, + "filterType": { + "type": "string" + }, + "isEntityType": { + "type": "boolean" + }, + "isMultipleFilter": { + "type": "boolean" + }, + "dataType": { + "type": "string" + }, + "defaultValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": ["label", "value"] + } + } + }, + "required": [ + "key", + "sort", + "label", + "filter", + "search", + "filterType", + "isEntityType", + "isMultipleFilter", + "defaultValues" + ] + } + } + } + }, + "organization_id": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_reports_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "report_type_title": { + "type": "string" + }, + "config": { + "type": "object", + "properties": {} + }, + "organization_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_reports_update_id": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "code": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "report_type_title": { + "type": "string" + }, + "config": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_reports_delete": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_reports_reportData": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "report_type": { + "type": "string" + }, + "config": { + "type": "object", + "properties": { + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "sort": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "filter": { + "type": "boolean" + }, + "search": { + "type": "boolean" + }, + "filterType": { + "type": "string" + }, + "isEntityType": { + "type": "boolean" + }, + "isMultipleFilter": { + "type": "boolean" + }, + "dataType": { + "type": "string" + }, + "defaultValues": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "key", + "sort", + "label", + "filter", + "search", + "filterType", + "isEntityType", + "isMultipleFilter", + "defaultValues" + ] + } + } + } + }, + "count": { + "type": "number" + }, + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sessions_title": { + "type": "string" + }, + "sessions_created_by": { + "type": "string" + }, + "number_of_mentees": { + "type": "number" + }, + "date_of_session": { + "type": "string" + }, + "session_type": { + "type": "string" + }, + "session_conducted": { + "type": "string" + }, + "duration_of_sessions_attended_in_minutes": { + "type": "string" + }, + "mentor_rating": { + "type": "string" + } + } + } + }, + "filters": { + "type": "object", + "properties": { + "sessions_title": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "sessions_created_by": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "number_of_mentees": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "date_of_session": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "session_type": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "session_conducted": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "duration_of_sessions_attended_in_minutes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "mentor_rating": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + } + } + }, + "reportsDownloadUrl": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_reports_filterList": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "entity_types": { + "type": "object", + "properties": { + "type": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "parent_id": { + "type": "null" + }, + "organization_id": { + "type": "string" + }, + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "entity_type_id": { + "type": "number" + }, + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + }, + "required": [ + "id", + "entity_type_id", + "value", + "label", + "status", + "type", + "created_by", + "updated_by", + "created_at", + "updated_at", + "deleted_at" + ] + } + } + } + } + }, + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "parent_id": { + "type": "null" + }, + "organization_id": { + "type": "string" + }, + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "entity_type_id": { + "type": "number" + }, + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + } + }, + "required": [ + "id", + "entity_type_id", + "value", + "label", + "status", + "type", + "created_by", + "updated_by", + "created_at", + "updated_at", + "deleted_at" + ] + } + } + } + } + } + } + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "label": { + "type": "string" + }, + "user_type": { + "type": "number" + }, + "status": { + "type": "string" + }, + "organization_id": { + "type": "number" + }, + "visibility": { + "type": "string" + } + }, + "required": ["id", "title", "label", "user_type", "status", "organization_id", "visibility"] + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/requestSessions/requestSessions.specs.js b/src/integration-tests-new/requestSessions/requestSessions.specs.js new file mode 100644 index 000000000..7be33efb9 --- /dev/null +++ b/src/integration-tests-new/requestSessions/requestSessions.specs.js @@ -0,0 +1,320 @@ +jest.setTimeout(60000) // Set default timeout to 30 seconds +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') +let menteeDetails = null // This user will make the request +let mentorDetails = null // This user will be the requestee +const schemas = require('./schemas/requestSessions.schemas.json') + +beforeAll(async () => { + console.log('setting up global variables....') + // Log in both a mentee and a mentor for the test + menteeDetails = await commonHelper.logIn() + mentorDetails = await commonHelper.mentorLogIn() + /* + let profileCreate = await request(BASE).post('/mentoring/v1/profile/create').set('x-auth-token', userDetails.token).send({ + designation: ['beo', 'deo', 'testt'], + area_of_expertise: ['educational_leadership', 'sqaa'], + education_qualification: 'MBA', + tags: ['Experienced', 'Technical'], + visibility: 'visible', + organisation_ids: [1], + external_session_visibility: 'CURRENT', + external_mentor_visibility: 'ALL', + }) + + console.log(profileCreate.body, 'profileCreatebody') + */ +}) + +describe('Session Request Lifecycle', () => { + let createdRequestSessionId + + describe('Create Session Request', () => { + test('POST /mentoring/v1/requestSessions/create - should return 201 on successful creation', async () => { + const url = `/mentoring/v1/requestSessions/create` // Removed query parameters + + // Create dynamic start and end dates + const now = new Date() + const startDate = new Date(now) + startDate.setDate(now.getDate() + 10) // 5 days in the future + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) // 1 hour duration + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + let req = request(BASE).post(url) + req = req + .set('x-auth-token', menteeDetails.token) // Use mentee's token + .set('org-id', menteeDetails.organizations[0]) // Correctly access the org ID string + .set('timezone', 'Asia/Calcutta') // Add timezone header + .send({ + title: 'test request session via jest', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + agenda: 'Dynamic agenda to teach chess basics', + requestee_id: mentorDetails.userId.toString(), // Use mentor's ID as the requestee + time_zone: 'Asia/Calcutta', + }) + + const res = await req + + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_mentoring_v1_requestSessions_create'] + expect(res.body).toMatchSchema(schema) + createdRequestSessionId = res.body.result.id // Capture the created request ID + expect(createdRequestSessionId).toBeDefined() + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/requestSessions/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', menteeDetails.token) + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('Accept Session Request', () => { + test('POST /mentoring/v1/requestSessions/accept - should return 201 when mentor accepts', async () => { + const url = `/mentoring/v1/requestSessions/accept?SkipValidation=true` + let req = request(BASE).post(url) + req = req + .set('x-auth-token', mentorDetails.token) // Use mentor's token to accept + .set('org-id', mentorDetails.organizations[0].id.toString()) // Correctly access the org ID string + .set('timezone', 'Asia/Calcutta') // Add timezone header + .send({ + request_session_id: createdRequestSessionId.toString(), + }) + + const res = await req + expect(res.status).toBe(201) + // validate response schema + expect(res.body).toMatchSchema(schemas['POST_mentoring_v1_requestSessions_accept']) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/accept` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) + +describe('Session Request Rejection Lifecycle', () => { + let createdRequestSessionId + + // This test creates a new session request that will be used in the rejection test below. + // It's good practice to isolate test lifecycles (e.g., accept vs. reject). + test('POST /mentoring/v1/requestSessions/create - should create a new request to be rejected', async () => { + const url = `/mentoring/v1/requestSessions/create` + + const now = new Date() + const startDate = new Date(now) + startDate.setDate(now.getDate() + 15) // 15 days in the future to avoid conflicts + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const res = await request(BASE) + .post(url) + .set('x-auth-token', menteeDetails.token) + .set('org-id', menteeDetails.organizations[0]) + .set('timezone', 'Asia/Calcutta') + .send({ + title: 'test request session for rejection via jest', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + agenda: 'Dynamic agenda to test rejection flow', + requestee_id: mentorDetails.userId.toString(), + time_zone: 'Asia/Calcutta', + }) + + expect(res.status).toBe(201) + createdRequestSessionId = res.body.result.id + expect(createdRequestSessionId).toBeDefined() + }) + + describe('Reject Session Request', () => { + test('POST /mentoring/v1/requestSessions/reject - should return 201 when mentor rejects', async () => { + const url = `/mentoring/v1/requestSessions/reject` + const res = await request(BASE) + .post(url) + .set('x-auth-token', mentorDetails.token) // Mentor's token to reject + .set('org-id', mentorDetails.organizations[0].id.toString()) + .send({ + request_session_id: createdRequestSessionId.toString(), + reject_reason: 'Scheduling conflict.', + }) + + expect(res.status).toBe(201) + + expect(res.body).toMatchSchema(schemas['POST_mentoring_v1_requestSessions_reject']) + }) + }) +}) + +describe('Standalone requestSessions endpoints', () => { + describe('GET /mentoring/v1/requestSessions/list', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/requestSessions/list?pageNo=1&pageSize=5` + let req = request(BASE).get(url) + req = req.set('x-auth-token', menteeDetails.token) + const res = await req + + expect(res.status).toBe(200) + // validate response schema + const schema = schemas['GET_mentoring_v1_requestSessions_list'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/list?pageNo=1&pageSize=5` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) + + /* + describe('GET /mentoring/v1/requestSessions/getDetails', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/requestSessions/getDetails?request_session_id=string`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "test-token"); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['GET_mentoring_v1_requestSessions_getDetails']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/getDetails?request_session_id=string`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + }); + + describe('GET /mentoring/v1/requestSessions/userAvailability', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/requestSessions/userAvailability?pageNo=string&pageSize=string&status=string&start_date=string&end_date=string`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', "test-token"); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['GET_mentoring_v1_requestSessions_userAvailability']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/userAvailability?pageNo=string&pageSize=string&status=string&start_date=string&end_date=string`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + }); + + describe('POST /mentoring/v1/requestSessions/accept', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/requestSessions/accept`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', "test-token"); + req = req.send({ + "request_session_id": "string" +}).set('Content-Type', 'application/json'); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['POST_mentoring_v1_requestSessions_accept']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/accept`; + const res = await request(BASE).post(url); + expect([401,403]).toContain(res.status); + }); + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/requestSessions/accept`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', "test-token"); + req = req.send({}).set('Content-Type', 'application/json'); + const res = await req; + expect([400,422]).toContain(res.status); + }); + + }); + + describe('POST /mentoring/v1/requestSessions/reject', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/requestSessions/reject`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', "test-token"); + req = req.send({ + "request_session_id": "string" +}).set('Content-Type', 'application/json'); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['POST_mentoring_v1_requestSessions_reject']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/requestSessions/reject`; + const res = await request(BASE).post(url); + expect([401,403]).toContain(res.status); + }); + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/requestSessions/reject`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', "test-token"); + req = req.send({}).set('Content-Type', 'application/json'); + const res = await req; + expect([400,422]).toContain(res.status); + }); + + }); + + */ +}) diff --git a/src/integration-tests-new/requestSessions/schemas/requestSessions.schemas.json b/src/integration-tests-new/requestSessions/schemas/requestSessions.schemas.json new file mode 100644 index 000000000..7fa797bb4 --- /dev/null +++ b/src/integration-tests-new/requestSessions/schemas/requestSessions.schemas.json @@ -0,0 +1,540 @@ +{ + "POST_mentoring_v1_requestSessions_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "id": { + "type": "number" + }, + "requestor_id": { + "type": "string" + }, + "requestee_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "title": { + "type": "string" + }, + "agenda": { + "type": "string" + }, + "start_date": { + "type": "number" + }, + "end_date": { + "type": "number" + }, + "created_by": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "meta": { + "type": "null" + }, + "session_id": { + "type": "null" + }, + "reject_reason": { + "type": "null" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_requestSessions_list": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "requestor_id": { + "type": "string" + }, + "requestee_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": ["object", "null"] + }, + "title": { + "type": "string" + }, + "agenda": { + "type": "string" + }, + "start_date": { + "type": "number" + }, + "end_date": { + "type": "number" + }, + "session_id": { + "type": ["string", "null"] + }, + "reject_reason": { + "type": "null" + }, + "updated_by": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "user_details": { + "type": "object", + "properties": { + "user_id": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "experience": { + "type": ["string", "null"] + }, + "designation": { + "type": ["string", "null"] + }, + "organization_id": { + "type": "string" + } + } + }, + "request_type": { + "type": "string" + } + } + } + }, + "count": { + "type": "number" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_requestSessions_getDetails": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "requestor_id": { + "type": "string" + }, + "requestee_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": {} + }, + "title": { + "type": "string" + }, + "agenda": { + "type": "string" + }, + "start_date": { + "type": "number" + }, + "end_date": { + "type": "number" + }, + "session_id": { + "type": "null" + }, + "reject_reason": { + "type": "null" + }, + "updated_by": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "user_details": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "mentee_visibility": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "designation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "area_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": ["value", "label"] + } + }, + "education_qualification": { + "type": "string" + }, + "meta": { + "type": "null" + }, + "is_mentor": { + "type": "boolean" + }, + "experience": { + "type": "string" + }, + "image": { + "type": "null" + } + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_requestSessions_userAvailability": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "bookedSlots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "startTime": { + "type": "string" + }, + "endTime": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": ["startTime", "endTime", "title"] + } + } + }, + "required": ["date", "bookedSlots"] + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_requestSessions_accept": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "string" + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_requestSessions_reject": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/role-extension/role-extension.spec.js b/src/integration-tests-new/role-extension/role-extension.spec.js new file mode 100644 index 000000000..b86d14330 --- /dev/null +++ b/src/integration-tests-new/role-extension/role-extension.spec.js @@ -0,0 +1,133 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/role-extension.schemas.json') + +describe('role-extension endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/role-extension/create', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/role-extension/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.set('x-auth-token', 'test-token') + req = req + .send({ + title: 'string', + label: 'string', + scope: 'string', + organization_id: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_role-extension_create'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/role-extension/create` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/role-extension/create` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.set('x-auth-token', 'test-token') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/role-extension/read', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/role-extension/read?title=mentee` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'bearer {{token}}') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_role-extension_read'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('POST /mentoring/v1/role-extension/update', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/role-extension/update?title=mentee` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req + .send({ + label: 'string', + scope: 'string', + organization_id: 'string', + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_role-extension_update'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/role-extension/update?title=mentee` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('DELETE /mentoring/v1/role-extension/delete', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/role-extension/delete?title=mentee` + let req = request(BASE).delete(url) + req = req.set('x-auth-token', 'bearer {{token}}') + req = req.set('x-auth-token', 'test-token') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['DELETE_mentoring_v1_role-extension_delete'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/role-extension/delete?title=mentee` + const res = await request(BASE).delete(url) + expect([401, 403]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/role-extension/schemas/role-extension.schemas.json b/src/integration-tests-new/role-extension/schemas/role-extension.schemas.json new file mode 100644 index 000000000..d3dd9c79c --- /dev/null +++ b/src/integration-tests-new/role-extension/schemas/role-extension.schemas.json @@ -0,0 +1,264 @@ +{ + "POST_mentoring_v1_role-extension_create": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "status": { + "type": "string" + }, + "title": { + "type": "string" + }, + "label": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "id": { + "type": "number" + }, + "deleted_at": { + "type": "null" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "GET_mentoring_v1_role-extension_read": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_role-extension_update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "DELETE_mentoring_v1_role-extension_delete": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "type": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": ["id", "type", "version"] + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js new file mode 100644 index 000000000..b494c3e3e --- /dev/null +++ b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js @@ -0,0 +1,75 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/rolePermissionMapping.schemas.json') + +describe('rolePermissionMapping endpoints generated from api-doc.yaml', () => { + describe('POST /mentoring/v1/rolePermissionMapping/create/{role_id}', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/rolePermissionMapping/create/{role_id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + permission_id: 1, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_rolePermissionMapping_create_role_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/rolePermissionMapping/create/{role_id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) + + describe('POST /mentoring/v1/rolePermissionMapping/delete/{role_id}', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/rolePermissionMapping/delete/{role_id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req + .send({ + permission_id: 1, + }) + .set('Content-Type', 'application/json') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['POST_mentoring_v1_rolePermissionMapping_delete_role_id'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + + test('should return 400/422 for invalid body', async () => { + const url = `/mentoring/v1/rolePermissionMapping/delete/{role_id}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', 'string') + req = req.send({}).set('Content-Type', 'application/json') + const res = await req + expect([400, 422]).toContain(res.status) + }) + }) +}) diff --git a/src/integration-tests-new/rolePermissionMapping/schemas/rolePermissionMapping.schemas.json b/src/integration-tests-new/rolePermissionMapping/schemas/rolePermissionMapping.schemas.json new file mode 100644 index 000000000..af20cf77d --- /dev/null +++ b/src/integration-tests-new/rolePermissionMapping/schemas/rolePermissionMapping.schemas.json @@ -0,0 +1,72 @@ +{ + "POST_mentoring_v1_rolePermissionMapping_create_role_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "roleId": { + "type": "number" + }, + "permissionId": { + "type": "number" + }, + "module": { + "type": "string" + }, + "request_type": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_mentoring_v1_rolePermissionMapping_delete_role_id": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": {} + }, + "meta": { + "type": "object", + "properties": { + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + } +} diff --git a/src/integration-tests-new/sessions/schemas/sessions.schemas.json b/src/integration-tests-new/sessions/schemas/sessions.schemas.json new file mode 100644 index 000000000..036d27661 --- /dev/null +++ b/src/integration-tests-new/sessions/schemas/sessions.schemas.json @@ -0,0 +1,499 @@ +{ + "GET_/mentoring/v1/sessions/list": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "count": { + "type": "number" + }, + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "mentor_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "status": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "meeting_info.value": { + "type": "string" + }, + "meeting_info.platform": { + "type": "string" + }, + "mentor_name": { + "type": "string" + }, + "organization_id": { + "type": "string" + } + } + } + } + } + } + } + }, + "GET_/mentoring/v1/sessions/details/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "mentor_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "status": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "created_at": { + "type": "string" + }, + "session_reschedule_count": { + "type": "number" + }, + "time_zone": { + "type": "string" + }, + "started_at": { + "type": "string" + }, + "completed_at": { + "type": "string" + }, + "is_feedback_skipped": { + "type": "boolean" + }, + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "meeting_info": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "platform": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": {} + }, + "visibility": { + "type": "string" + }, + "organization_ids": { + "type": "string" + }, + "mentor_organization_id": { + "type": "string" + }, + "seats_remaining": { + "type": "number" + }, + "seats_limit": { + "type": "number" + }, + "updated_at": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "is_enrolled": { + "type": "boolean" + }, + "mentor_name": { + "type": "string" + }, + "organization_id": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "meta": { + "type": "object", + "properties": {} + } + } + }, + "GET_/mentoring/v1/sessions/share/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "share_link": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": {} + } + } + }, + "GET_/mentoring/v1/mentees/joinSession/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "link": { + "type": "string" + } + } + }, + "meta": { + "type": "object", + "properties": { + "formsVersion": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "type": { "type": "string" }, + "version": { "type": "number" } + } + } + }, + "correlation": { + "type": "string" + }, + "meetingPlatform": { + "type": "string" + } + } + } + } + }, + "POST_/mentoring/v1/sessions/enroll/{sessionId}": {}, + "POST_/mentoring/v1/sessions/unenroll/{sessionId}": {}, + "GET_/mentoring/v1/sessions/start/{sessionId}": {}, + "POST_/mentoring/v1/sessions/update": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "recommended_for": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "medium": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "mentor_id": { + "type": "string" + }, + "mentor_name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "is_feedback_skipped": { + "type": "boolean" + }, + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "id": { + "type": "number" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + } + } + }, + "DELETE_/mentoring/v1/sessions/update/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "properties": {} + } + } + }, + "POST_/mentoring/v1/sessions/update/{sessionId}": { + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "end_date": { + "type": "string" + }, + "recommended_for": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "medium": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "image": { + "type": "array", + "items": { + "type": "string" + } + }, + "mentor_id": { + "type": "string" + }, + "mentor_name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deleted_at": { + "type": "null" + }, + "is_feedback_skipped": { + "type": "boolean" + }, + "mentee_feedback_question_set": { + "type": "string" + }, + "mentor_feedback_question_set": { + "type": "string" + }, + "id": { + "type": "number" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + } + } + }, + "GET_/mentoring/v1/sessions/getRecording/{sessionId}": {}, + "PATCH_/mentoring/v1/sessions/completed/{sessionId}": {}, + "PATCH_/mentoring/v1/sessions/updateRecordingUrl/{internalSessionId}": {} +} diff --git a/src/integration-tests-new/sessions/sessions.specs.js b/src/integration-tests-new/sessions/sessions.specs.js new file mode 100644 index 000000000..af8061b99 --- /dev/null +++ b/src/integration-tests-new/sessions/sessions.specs.js @@ -0,0 +1,596 @@ +jest.setTimeout(100000) +const request = require('supertest') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const commonHelper = require('@commonTests') + +let userDetails = null +let menteeDetails = null + +const schemas = require('./schemas/sessions.schemas.json') + +beforeAll(async () => { + userDetails = await commonHelper.mentorLogIn() // Log in a mentor user + menteeDetails = await commonHelper.logIn() // Log in a second user to act as a mentee (menteeDetails) + + console.log(userDetails) + console.log(menteeDetails) + console.log('line 18') +}) + +describe('sessions endpoints generated from api-doc.yaml', () => { + describe('Session Details Lifecycle', () => { + let createdSessionId + + beforeAll(async () => { + // Create a session to be used in the tests + const now = new Date() + const startDate = new Date(now) + startDate.setDate(now.getDate() + 3) + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createUrl = `/mentoring/v1/sessions/update` + const createRes = await request(BASE).post(createUrl).set('x-auth-token', userDetails.token).send({ + title: 'test nov 27', + description: 'desc', + type: 'PUBLIC', + mentees: [], + start_date: startDateTimestamp, + end_date: endDateTimestamp, + recommended_for: [], + categories: [], + medium: [], + time_zone: 'Asia/Calcutta', + mentor_id: userDetails.userId.toString(), + }) + + // Assuming 201 is the success status for creation + console.log(createRes, 'create status*') + expect(createRes.status).toBe(201) + createdSessionId = createRes.body.result.id + expect(createdSessionId).toBeDefined() + }) + + afterAll(async () => { + // Clean up the created session + if (createdSessionId) { + const deleteUrl = `/mentoring/v1/sessions/update/${createdSessionId}` + // We don't need to assert the result of cleanup, but it's good practice to ensure it runs + // await request(BASE).delete(deleteUrl).set('x-auth-token', userDetails.token) + } + }) + + test('GET /mentoring/v1/sessions/details/{sessionId} - should return 200 on success', async () => { + const url = `/mentoring/v1/sessions/details/${createdSessionId}` + let req = request(BASE).get(url) + req = req.set('x-auth-token', userDetails.token) // Make the API call + const res = await req + expect(res.status).toBe(201) + }) + + test('GET /mentoring/v1/sessions/details/{sessionId} - should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/details/${createdSessionId}` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + + test('POST /mentoring/v1/sessions/enroll/{sessionId} - should return 200 on successful enrollment', async () => { + const url = `/mentoring/v1/sessions/enroll/${createdSessionId}` + let req = request(BASE).post(url) + req = req.set('x-auth-token', menteeDetails.token).set('timezone', 'Asia/Calcutta') // Use mentee's token and add timezone // Make the API call + const res = await req + expect(res.status).toBe(201) + // validate response schema + const schema = schemas['POST_/mentoring/v1/sessions/enroll/{sessionId}'] + expect(res.body).toMatchSchema(schema) + }) + + test('POST /mentoring/v1/sessions/enroll/{sessionId} - should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/enroll/${createdSessionId}` + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + + test('POST /mentoring/v1/sessions/unenroll/{sessionId} - should return 200 on successful unenrollment', async () => { + const url = `/mentoring/v1/sessions/unEnroll/${createdSessionId}` // Corrected to camelCase 'unEnroll' + let req = request(BASE).post(url) + req = req.set('x-auth-token', menteeDetails.token) // Use mentee's token to unenroll // Make the API call + const res = await req + expect(res.status).toBe(202) + // validate response schema + const schema = schemas['POST_/mentoring/v1/sessions/unenroll/{sessionId}'] + expect(res.body).toMatchSchema(schema) + }) + + test('POST /mentoring/v1/sessions/unenroll/{sessionId} - should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/unEnroll/${createdSessionId}` // Corrected to camelCase 'unEnroll' + const res = await request(BASE).post(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('GET /mentoring/v1/sessions/start/{sessionId}', () => { + test('should return 200 on successful session start', async () => { + let mentorDetails = await commonHelper.mentorLogIn() + // Step 1: Create a session that is scheduled to start now + const now = new Date() + const startDate = new Date(now.getTime() - 10 * 1000) // 10 seconds ago + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createRes = await request(BASE) + .post('/mentoring/v1/sessions/update') + .set('x-auth-token', mentorDetails.token) + .send({ + title: 'Session to be started', + description: 'desc', + type: 'PUBLIC', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + time_zone: 'Asia/Calcutta', + mentor_id: mentorDetails.userId.toString(), + }) + + expect(createRes.status).toBe(201) + const createdSessionId = createRes.body.result.id + + // Step 2: Start the created session + const url = `/mentoring/v1/sessions/start/${createdSessionId}` + let req = request(BASE).get(url) + req = req.set('x-auth-token', mentorDetails.token) // Make the API call + const res = await req + expect(res.status).toBe(200) + const schema = schemas['GET_/mentoring/v1/sessions/start/{sessionId}'] + expect(res.body).toMatchSchema(schema) + }) + }) + + describe('GET /mentoring/v1/mentees/joinSession/{sessionId}', () => { + test('should return 200 when a mentee joins a started session', async () => { + const mentorDetails = await commonHelper.mentorLogIn() + const menteeDetails = await commonHelper.logIn() + + // Step 1: Mentor creates a session scheduled to start now + const now = new Date() + const startDate = new Date(now.getTime() - 10 * 1000) // 10 seconds ago + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createRes = await request(BASE) + .post('/mentoring/v1/sessions/update') + .set('x-auth-token', mentorDetails.token) // Mentor's token + .send({ + title: 'Session to be Joined', + description: 'desc', + type: 'PUBLIC', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + time_zone: 'Asia/Calcutta', + mentor_id: mentorDetails.userId.toString(), + }) + expect(createRes.status).toBe(201) + const sessionId = createRes.body.result.id + + // Step 2: Mentor starts the session + const startRes = await request(BASE) + .get(`/mentoring/v1/sessions/start/${sessionId}`) + .set('x-auth-token', mentorDetails.token) + + // Step 3: Mentee enrolls in the session + const enrollUrl = `/mentoring/v1/sessions/enroll/${sessionId}` + const enrollRes = await request(BASE) + .post(enrollUrl) + .set('x-auth-token', menteeDetails.token) + .set('timezone', 'Asia/Calcutta') + expect(enrollRes.status).toBe(201) + + // Step 4: Mentee joins the session + const url = `/mentoring/v1/mentees/joinSession/${sessionId}` + let req = request(BASE).get(url) + req = req.set('x-auth-token', menteeDetails.token) // Mentee's token + req = req.set('org-id', menteeDetails.organizations[0].id.toString()) + req = req.set('timezone', 'Asia/Calcutta') + + const res = await req + expect(res.status).toBe(200) + + const schema = schemas['GET_/mentoring/v1/mentees/joinSession/{sessionId}'] + expect(res.body).toMatchSchema(schema) + }) + + test('should return 401/403 when trying to join without a token', async () => { + const mentorDetails = await commonHelper.mentorLogIn() + + // Step 1: Mentor creates a session + const now = new Date() + const startDate = new Date(now.getTime() - 10 * 1000) // 10 seconds ago + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + const endDate = new Date(startDate) + endDate.setHours(startDate.getHours() + 1) + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createRes = await request(BASE) + .post('/mentoring/v1/sessions/update') + .set('x-auth-token', mentorDetails.token) + .send({ + title: 'Session for unauthorized join test', + description: 'desc', + type: 'PUBLIC', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + time_zone: 'Asia/Calcutta', + mentor_id: mentorDetails.userId.toString(), + }) + expect(createRes.status).toBe(201) + const sessionId = createRes.body.result.id + + // Step 2: Attempt to join the session without a token + const url = `/mentoring/v1/mentees/joinSession/${sessionId}` + const res = await request(BASE).get(url) + expect([401, 403]).toContain(res.status) + }) + }) + + describe('PATCH /mentoring/v1/sessions/completed/{sessionId}', () => { + let sessionId + + beforeAll(async () => { + // Create a session that has passed to mark as complete + const now = new Date() + const startDate = new Date(now.getTime() - 2 * 60 * 60 * 1000) // 2 hours ago + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + const endDate = new Date(startDate.getTime() + 60 * 60 * 1000) // 1 hour duration + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createRes = await request(BASE) + .post('/mentoring/v1/sessions/update') + .set('x-auth-token', userDetails.token) + .send({ + title: 'Session to be completed', + description: 'This session is created to test the completion endpoint.', + type: 'PUBLIC', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + time_zone: 'Asia/Calcutta', + mentor_id: userDetails.userId.toString(), + }) + + expect(createRes.status).toBe(201) + sessionId = createRes.body.result.id + }) + + test('should return 200 on success', async () => { + // First, start the session + const startUrl = `/mentoring/v1/sessions/start/${sessionId}` + const startRes = await request(BASE).get(startUrl).set('x-auth-token', userDetails.token) + expect(startRes.status).toBe(200) + + // Then, mark the session as completed + const url = `/mentoring/v1/sessions/completed/${sessionId}` + let req = request(BASE).patch(url) + req = req.set('x-auth-token', userDetails.token) // Make the API call + const res = await req + expect(res.status).toBe(200) + // const schema = schemas['PATCH_/mentoring/v1/sessions/completed/{sessionId}'] + // expect(res.body).toMatchSchema(schema) + }) + }) + + describe('Session Feedback Submission', () => { + let feedbackSessionId + + beforeAll(async () => { + let userDetails = await commonHelper.mentorLogIn() + // Create a session that has passed to mark as complete + const now = new Date() + const startDate = new Date(now.getTime() - 2 * 60 * 60 * 1000) // 2 hours ago + const startDateTimestamp = Math.floor(startDate.getTime() / 1000) + const endDate = new Date(startDate.getTime() + 60 * 60 * 1000) // 1 hour duration + const endDateTimestamp = Math.floor(endDate.getTime() / 1000) + + const createRes = await request(BASE) + .post('/mentoring/v1/sessions/update') + .set('x-auth-token', userDetails.token) + .send({ + title: 'Session for Feedback Test', + description: 'This session is created to test the feedback endpoint.', + type: 'PUBLIC', + start_date: startDateTimestamp, + end_date: endDateTimestamp, + time_zone: 'Asia/Calcutta', + mentor_id: userDetails.userId.toString(), + }) + + expect(createRes.status).toBe(201) + feedbackSessionId = createRes.body.result.id + + // Enroll mentee in the session + const enrollUrl = `/mentoring/v1/sessions/enroll/${feedbackSessionId}` + const enrollRes = await request(BASE) + .post(enrollUrl) + .set('x-auth-token', menteeDetails.token) + .set('timezone', 'Asia/Calcutta') + expect(enrollRes.status).toBe(201) + + // Start the session + const startUrl = `/mentoring/v1/sessions/start/${feedbackSessionId}` + const startRes = await request(BASE).get(startUrl).set('x-auth-token', userDetails.token) + expect(startRes.status).toBe(200) + + // Complete the session + const completeUrl = `/mentoring/v1/sessions/completed/${feedbackSessionId}` + const completeRes = await request(BASE).patch(completeUrl).set('x-auth-token', userDetails.token) + expect(completeRes.status).toBe(200) + }) + + test('POST /mentoring/v1/feedback/submit/{sessionId} - mentee feedback should return 200', async () => { + const url = `/mentoring/v1/feedback/submit/${feedbackSessionId}` + const menteeFeedbackPayload = { + feedbacks: [ + { question_id: 3, value: 3, label: 'How would you rate the host of the session?' }, + { question_id: 4, value: 4, label: 'How would you rate the engagement in the session?' }, + ], + feedback_as: 'mentee', + } + const res = await request(BASE) + .post(url) + .set('x-auth-token', menteeDetails.token) + .send(menteeFeedbackPayload) + expect(res.status).toBe(200) + }) + + test('POST /mentoring/v1/feedback/submit/{sessionId} - mentor feedback should return 200', async () => { + const url = `/mentoring/v1/feedback/submit/${feedbackSessionId}` + const mentorFeedbackPayload = { + feedbacks: [ + { question_id: 1, value: 4, label: 'How would you rate the engagement in the session?' }, + { question_id: 2, value: 3, label: 'How would you rate the Audio/Video quality?' }, + ], + feedback_as: 'mentor', + } + const res = await request(BASE).post(url).set('x-auth-token', userDetails.token).send(mentorFeedbackPayload) + expect(res.status).toBe(200) + }) + }) + /* + describe('GET /mentoring/v1/sessions/list', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/sessions/list?page=1&limit=2&status=PUBLISHED, COMPLETED&search=John&recommended_for=string`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', userDetails.token); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['GET_/mentoring/v1/sessions/list']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/list?page=1&limit=2&status=PUBLISHED, COMPLETED&search=John&recommended_for=string`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('GET /mentoring/v1/sessions/share/{sessionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/sessions/share/1`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', userDetails.token); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['GET_/mentoring/v1/sessions/share/{sessionId}']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/share/1`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + */ + /* + describe('POST /mentoring/v1/sessions/update', () => { + test('should return 201', async () => { + const url = `/mentoring/v1/sessions/update`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', userDetails.token); + req = req.send({ + "title": "Leadership session by Adam", + "description": "Leadership session desc", + "start_date": "1695210731", + "end_date": "1695214329", + "mentee_feedback_question_set": "MENTEE_QS1", + "mentor_feedback_question_set": "MENTOR_QS2", + "recommended_for": [ + "deo" + ], + "categories": [ + "educational_leadership" + ], + "medium": [ + "en" + ], + "image": [ + "users/1232s2133sdd1-12e2dasd3123.png" + ] + }).set('Content-Type', 'application/json'); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['POST_/mentoring/v1/sessions/update']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/update`; + const res = await request(BASE).post(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('DELETE /mentoring/v1/sessions/update/{sessionId}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/sessions/update/1`; + let req = request(BASE).delete(url); + req = req.set('x-auth-token', userDetails.token); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['DELETE_/mentoring/v1/sessions/update/{sessionId}']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/update/1`; + const res = await request(BASE).delete(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('POST /mentoring/v1/sessions/update/{sessionId}', () => { + test('should return 202', async () => { + const url = `/mentoring/v1/sessions/update/1`; + let req = request(BASE).post(url); + req = req.set('x-auth-token', userDetails.token); + req = req.send({ + "title": "Leadership session by Adam", + "description": "Leadership session desc", + "start_date": "1695210731", + "end_date": "1695214329", + "mentee_feedback_question_set": "MENTEE_QS1", + "mentor_feedback_question_set": "MENTOR_QS2", + "recommended_for": [ + "deo" + ], + "categories": [ + "educational_leadership" + ], + "medium": [ + "en" + ], + "image": [ + "users/1232s2133sdd1-12e2dasd3123.png" + ] + }).set('Content-Type', 'application/json'); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['POST_/mentoring/v1/sessions/update/{sessionId}']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/update/1`; + const res = await request(BASE).post(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('GET /mentoring/v1/sessions/getRecording/{sessionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/sessions/getRecording/1`; + let req = request(BASE).get(url); + req = req.set('x-auth-token', userDetails.token); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['GET_/mentoring/v1/sessions/getRecording/{sessionId}']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/getRecording/1`; + const res = await request(BASE).get(url); + expect([401,403]).toContain(res.status); + }); + + + }); + + describe('PATCH /mentoring/v1/sessions/updateRecordingUrl/{internalSessionId}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/sessions/updateRecordingUrl/1`; + let req = request(BASE).patch(url); + req = req.set('x-auth-token', userDetails.token); + const res = await req; + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + // validate response schema + const schema = schemas['PATCH_/mentoring/v1/sessions/updateRecordingUrl/{internalSessionId}']; + const validate = ajv.compile(schema); + const valid = validate(res.body); + if (!valid) { + console.error("Schema validation errors:", validate.errors); + } + expect(valid).toBe(true); + }); + + test('should return 401/403 when unauthorized', async () => { + const url = `/mentoring/v1/sessions/updateRecordingUrl/1`; + const res = await request(BASE).patch(url); + expect([401,403]).toContain(res.status); + }); + + + }); +*/ +}) diff --git a/src/integration-tests-new/users/schemas/users.schemas.json b/src/integration-tests-new/users/schemas/users.schemas.json new file mode 100644 index 000000000..01fa87e27 --- /dev/null +++ b/src/integration-tests-new/users/schemas/users.schemas.json @@ -0,0 +1,100 @@ +{ + "GET_mentoring_v1_users_pendingFeedbacks": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mentor_feedback_form": { + "type": "string" + }, + "form": { + "type": "object", + "properties": {} + } + } + } + } + } + }, + "GET_mentoring_v1_users_list_type_userType_page_page_limit_limit_search_search": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "responseCode": { + "type": "string" + }, + "message": { + "type": "string" + }, + "result": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string" + }, + "areas_of_expertise": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "image": { + "type": "string" + } + } + } + } + } + } + }, + "count": { + "type": "number" + } + } + } + } + } +} diff --git a/src/integration-tests-new/users/users.spec.js b/src/integration-tests-new/users/users.spec.js new file mode 100644 index 000000000..5bfb1cfa7 --- /dev/null +++ b/src/integration-tests-new/users/users.spec.js @@ -0,0 +1,47 @@ +const request = require('supertest') +const Ajv = require('ajv') +const BASE = process.env.BASE_URL || 'http://localhost:3000' +const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' +const ajv = new Ajv({ strict: false }) + +const schemas = require('./schemas/users.schemas.json') + +describe('users endpoints generated from api-doc.yaml', () => { + describe('GET /mentoring/v1/users/pendingFeedbacks', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/users/pendingFeedbacks` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_users_pendingFeedbacks'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) + + describe('GET /mentoring/v1/users/list?type={userType}&page={page}&limit={limit}&search={search}', () => { + test('should return 200', async () => { + const url = `/mentoring/v1/users/list?type=mentor&page=1&limit=2&search=jhon` + let req = request(BASE).get(url) + req = req.set('x-auth-token', 'string') + const res = await req + expect(res.status).toBeGreaterThanOrEqual(200) + expect(res.status).toBeLessThan(300) + // validate response schema + const schema = schemas['GET_mentoring_v1_users_list_type_userType_page_page_limit_limit_search_search'] + const validate = ajv.compile(schema) + const valid = validate(res.body) + if (!valid) { + console.error('Schema validation errors:', validate.errors) + } + expect(valid).toBe(true) + }) + }) +}) diff --git a/src/integrationJest.config.js b/src/integrationJest.config.js index 79e092584..1c1a2d797 100644 --- a/src/integrationJest.config.js +++ b/src/integrationJest.config.js @@ -15,9 +15,11 @@ module.exports = { '@constants/(.*)': '/constants/$1', '@configs/(.*)': '/configs/$1', '@health-checks/(.*)': '/health-checks/$1', - '@commonTests': '/integration-tests/commonTests', + '@commonTests': '/integration-tests-new/commonTests', + '@helpers/(.*)': '/helpers/$1', + '@utils/(.*)': '/utils/$1', }, - testMatch: ['/integration-tests/**/*.spec.js'], + testMatch: ['/integration-tests-new/**/*.specs.js'], reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: '../dev-ops/report' }]], } /* Add env variables used by jest here because jest do not have access to app or docker env files. diff --git a/src/package.json b/src/package.json index a77226f14..130dd5923 100644 --- a/src/package.json +++ b/src/package.json @@ -14,8 +14,10 @@ "qa": "NODE_ENV=qa node app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", - "db:init": "sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check' && sequelize-cli db:migrate ", - "db:seed:all": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' " + "db:init:old": "(sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check') && sequelize-cli db:migrate", + "db:init": "sequelize-cli db:create || echo 'exists'; node scripts/run-migrations.js", + "db:seed:all:old": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' ", + "db:seed:all": "node scripts/run-seeders.js" }, "author": "Aman Kumar Gupta ", "license": "ISC", diff --git a/src/scripts/run-migrations.js b/src/scripts/run-migrations.js new file mode 100644 index 000000000..ffc83d615 --- /dev/null +++ b/src/scripts/run-migrations.js @@ -0,0 +1,30 @@ +const Umzug = require('umzug') +const path = require('path') +const { sequelize } = require('../database/models/index') // adjust path if needed + +async function runMigrations() { + const umzug = new Umzug({ + storage: 'sequelize', + storageOptions: { + sequelize, + }, + migrations: { + path: path.join(__dirname, '../database/migrations'), + pattern: /\.js$/, + params: [sequelize.getQueryInterface(), sequelize.constructor], + }, + logging: console.log, + }) + + try { + console.log('Starting migrations...') + await umzug.up() + console.log('MIGRATION_SUCCESS') + process.exit(0) + } catch (err) { + console.error('Migration failed:', err) + process.exit(1) + } +} + +runMigrations() diff --git a/src/scripts/run-seeders.js b/src/scripts/run-seeders.js new file mode 100644 index 000000000..7a8a588c8 --- /dev/null +++ b/src/scripts/run-seeders.js @@ -0,0 +1,30 @@ +const Umzug = require('umzug') +const path = require('path') +const { sequelize } = require('../database/models/index') // adjust path if needed + +async function runSeeders() { + const umzug = new Umzug({ + storage: 'sequelize', + storageOptions: { + sequelize, + }, + migrations: { + path: path.join(__dirname, '../database/seeders'), + pattern: /\.js$/, + params: [sequelize.getQueryInterface(), sequelize.constructor], + }, + logging: console.log, + }) + + try { + console.log('Running seeders...') + await umzug.up() // <-- BLOCKS until all seeds are done + console.log('SEEDER_SUCCESS') + process.exit(0) + } catch (err) { + console.error('Seeding failed:', err) + process.exit(1) + } +} + +runSeeders() diff --git a/src/start.sh b/src/start.sh new file mode 100755 index 000000000..2830e1528 --- /dev/null +++ b/src/start.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +echo "[MENTORING] Running migrations..." +npm run db:init + +echo "[MENTORING] Running seeds..." +npm run db:seed:all + +echo "[MENTORING] Starting application..." +npm run dev From 5344ae37a6a3da753042e959777d523676b29408 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 10 Dec 2025 23:32:26 +0530 Subject: [PATCH 02/66] updated devops files --- dev-ops/docker-compose.yml | 141 ++++++++++++++++--------- dev-ops/integration_test.mentoring.env | 52 +++++++-- dev-ops/integration_test.user.env | 39 ++++++- 3 files changed, 169 insertions(+), 63 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 55d9dea25..a311e7018 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -1,101 +1,144 @@ version: '3' services: + + redis: + image: 'redis:7.0.0' + restart: 'always' + ports: + - '6379:6379' + networks: + - elevate_net + logging: + driver: local + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 20 + zookeeper: - image: 'bitnami/zookeeper:3.8.0' + image: 'confluentinc/cp-zookeeper:7.3.0' ports: - '2181:2181' environment: - - ALLOW_ANONYMOUS_LOGIN=yes + - ZOOKEEPER_CLIENT_PORT=2181 + - ZOOKEEPER_TICK_TIME=2000 networks: - elevate_net - volumes: - - zookeeper-data:/bitnami/zookeeper logging: driver: none + kafka: - image: 'bitnami/kafka:3.1.0' + image: 'confluentinc/cp-kafka:7.3.0' ports: - '9092:9092' environment: - - KAFKA_BROKER_ID=1 - - KAFKA_CFG_LISTENERS=CLIENT://:9092,EXTERNAL://:9093 - - KAFKA_CFG_ADVERTISED_LISTENERS=CLIENT://kafka:9092,EXTERNAL://localhost:9093 - - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - - ALLOW_PLAINTEXT_LISTENER=yes - - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CLIENT:PLAINTEXT,EXTERNAL:PLAINTEXT - - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=CLIENT + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + # Listeners configured for CI internal docker networking + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:9093 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 depends_on: - zookeeper networks: - elevate_net - volumes: - - kafka-data:/bitnami/kafka + healthcheck: + test: kafka-topics --bootstrap-server localhost:9092 --list || exit 1 + interval: 10s + timeout: 30s + retries: 10 logging: - driver: none - mongo: - image: 'mongo:4.4.14' - restart: 'always' + driver: local + + # NEW: Citus DB (Fresh instance, no volumes) + citus: + image: citusdata/citus:13.0 + container_name: citus_db ports: - - '27017:27017' - networks: - - elevate_net - volumes: - - mongo-data:/data/db - logging: - driver: none - redis: - image: 'redis:7.0.0' - restart: 'always' - expose: - - '6379' + - '5432:5432' + environment: + POSTGRES_USER: 'postgres' + POSTGRES_PASSWORD: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' networks: - elevate_net - logging: - driver: none + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U postgres'] + interval: 10s + timeout: 5s + retries: 5 + + # ---------------- APPLICATION SERVICES ---------------- + mentoring: - build: '../' - image: elevate/mentoring:1.0 + build: '../' # Preserved from your old file + image: elevate/mentoring:2.4 volumes: - ../src/:/var/src ports: - '3000:3000' - command: [ 'node', 'app.js' ] + - '9229:9229' + command: ["sh", "/var/src/start.sh"] + environment: - - MONGODB_URL=mongodb://mongo:27017/elevate-mentoring - KAFKA_URL=kafka:9092 - - USER_SERIVCE_HOST=http://user:3001 + - USER_SERVICE_HOST=http://user:3001 - REDIS_HOST=redis://redis:6379 + - SCHEDULER_SERVICE_HOST=http://scheduler:4000 + - DEV_DATABASE_URL=postgres://postgres:postgres@citus:5432/elevate-mentoring + - ENABLE_CHAT=false + - ADMIN_SECRET_CODE=ADMIN_SECRET_CODE depends_on: - - kafka - - mongo + kafka: + condition: service_healthy + citus: + condition: service_healthy + redis: + condition: service_healthy networks: - elevate_net env_file: - - integration_test.mentoring.env + - ./integration_test.mentoring.env + user: - build: '../../user/' - image: elevate/user:1.0 + build: '../../user/' # Preserved from your old file + image: elevate/user:2.4 volumes: - ../../user/src/:/var/src ports: - '3001:3001' - command: [ 'nodemon', 'app.js' ] + # - '9229:9229' + command: > + sh -c "sleep 20 && + npm run db:init && + npm run db:seed:all && + nodemon --trace-warnings app.js" environment: - - MONGODB_URL=mongodb://mongo:27017/elevate-mentoring - KAFKA_URL=kafka:9092 - REDIS_HOST=redis://redis:6379 + - SCHEDULER_SERVICE_HOST=http://scheduler:4000 + - DEV_DATABASE_URL=postgres://postgres:postgres@citus:5432/mentoring_user + - ENABLE_EMAIL_OTP_VERIFICATION=false + - ADMIN_SECRET_CODE=ADMIN_SECRET_CODE depends_on: - - kafka - - mongo - - redis + kafka: + condition: service_healthy + citus: + condition: service_healthy + redis: + condition: service_healthy networks: - elevate_net env_file: - - integration_test.user.env + - ./integration_test.user.env + networks: elevate_net: external: false + volumes: zookeeper-data: kafka-data: - mongo-data: + # No DB volumes here = Fresh DB every time \ No newline at end of file diff --git a/dev-ops/integration_test.mentoring.env b/dev-ops/integration_test.mentoring.env index 408981b17..362d61c2a 100644 --- a/dev-ops/integration_test.mentoring.env +++ b/dev-ops/integration_test.mentoring.env @@ -1,8 +1,8 @@ # Mentoring Service Config - +ALLOWED_HOST="*" # Port on which service runs APPLICATION_PORT = 3000 - +APPLICATION_HOST="localhost" #Service environment APPLICATION_ENV = development @@ -10,20 +10,20 @@ APPLICATION_ENV = development APPLICATION_BASE_URL = /mentoring/ #Mongo db connectivity url -MONGODB_URL = mongodb://localhost:27017/elevate-mentoring +MONGODB_URL = mongodb://localhost:27017/mentoring-local #Token secret to verify the access token ACCESS_TOKEN_SECRET = 'bsj82AHBxahusub12yexlashsbxAXADHBlaj' # Internal access token for communicationcation between services via network call -INTERNAL_ACCESS_TOKEN = 'internal-access-token' +INTERNAL_ACCESS_TOKEN = 'internal_access_token' # Kafka hosted server url -KAFKA_URL = localhost:9092 +KAFKA_URL = kafka:9092 # Kafka group to which consumer belongs -KAFKA_GROUP_ID = userservice - +KAFKA_GROUP_ID = qa.mentoring +KAFKA_TOPIC="qa.topic" # Kafka topic to push notification data NOTIFICATION_KAFKA_TOPIC = 'testTopic' @@ -35,7 +35,13 @@ KAFKA_RECORDING_TOPIC ="recordingtopic" # Any one of three features available for cloud storage CLOUD_STORAGE = 'GCP/AWS/AZURE' - +CLOUD_STORAGE_ACCOUNTNAME = '****' +CLOUD_STORAGE_BUCKETNAME = '****' +CLOUD_STORAGE_BUCKET_TYPE = '****' +CLOUD_STORAGE_PROJECT = "CLOUD_STORAGE_PROJECT" +CLOUD_STORAGE_PROVIDER = "gcloud" +CLOUD_STORAGE_SECRET = "CLOUD_STORAGE_SECRET" +COMMUNICATION_SERVICE_HOST="http://localhost:3123" # Gcp json config file path GCP_PATH = 'gcp.json' @@ -101,7 +107,7 @@ API_DOC_URL = '/api-doc' INTERNAL_CACHE_EXP_TIME = 86400 # Redis Host connectivity url -REDIS_HOST = 'redis://localhost:6379' +REDIS_HOST = 'redis://redis:6379' #Kafka internal communicationcation CLEAR_INTERNAL_CACHE = 'mentoringInternal' @@ -122,3 +128,31 @@ DISABLE_LOG=false DEFAULT_MEETING_SERVICE="BBB" SESSION_EDIT_WINDOW_MINUTES=0 SESSION_MENTEE_LIMIT=0 +DEFAULT_ORG_ID=1 +DISABLE_SESSION_VALIDATION = "true" +SESSION_VERIFICATION_METHOD = "disabled" +EMAIL_ID_ENCRYPTION_IV="c9c7bd480494409071847264652f5c95" +EMAIL_ID_ENCRYPTION_KEY="eef7e009626c18724be86afa41a2620e0718561a508c61f92d7ee0377177ef7b" +ENABLE_CHAT="false" +IS_AUTH_TOKEN_BEARER="false" +MENTOR_ACCEPT_SESSION_REQUEST_EMAIL_TEMPLATE="request_session_accepted_email_template" +MENTOR_REJECT_SESSION_REQUEST_EMAIL_TEMPLATE="request_session_rejected_email_template" +MENTOR_PRIVATE_SESSION_INVITE_BY_MANAGER_EMAIL_TEMPLATE="mentor_invite_private_session_by_manager" +MENTOR_PUBLIC_SESSION_INVITE_BY_MANAGER_EMAIL_TEMPLATE="mentor_invite_public_session_by_manager" +PORTAL_BASE_URL = "PORTAL_BASE_URL" +PUBLIC_ASSET_BUCKETNAME="PUBLIC_ASSET_BUCKETNAME" +RATING_KAFKA_TOPIC="RATING_KAFKA_TOPIC" +SCHEDULER_SERVICE_ERROR_REPORTING_EMAIL_ID="rakesh.k@pacewisdom.com" +SCHEDULER_SERVICE_HOST="SCHEDULER_SERVICE_HOST" +SCHEDULER_SERVICE_BASE_URL = "/scheduler/" +SCHEDULER_SERVICE_URL = "SCHEDULER_SERVICE_URL" +SESSION_MENTEE_LIMIT = "5" +SUPPORT_EMAIL_ID="support@shikshalokam.org" +USER_SERVICE_HOST="http://user:3001" +USER_SERVICE_BASE_URL="/user/" +DEFAULT_TENANT_CODE="default" +DEFAULT_ORGANIZATION_CODE="default_code" +AUTH_METHOD="native" + +EVENTS_TOPIC="dev.userCreate" +MONGODB_URL=mongodb://mongo:27017/mentoring-local diff --git a/dev-ops/integration_test.user.env b/dev-ops/integration_test.user.env index 00d03612e..664043d43 100644 --- a/dev-ops/integration_test.user.env +++ b/dev-ops/integration_test.user.env @@ -16,13 +16,13 @@ ACCESS_TOKEN_SECRET = 'bsj82AHBxahusub12yexlashsbxAXADHBlaj' REFRESH_TOKEN_SECRET = 'refresh-token-secret' # Kafka hosted server url -KAFKA_URL = localhost:9092 +KAFKA_URL = kafka:9092 # Kafka group to which consumer belongs -KAFKA_GROUP_ID = userservice +KAFKA_GROUP_ID = qa.users # Kafka topic to consume data from -KAFKA_TOPIC = 'testTopic' +KAFKA_TOPIC = 'qa.topic' # Kafka topic to push notification data NOTIFICATION_KAFKA_TOPIC = 'testTopic' @@ -64,7 +64,7 @@ AZURE_ACCOUNT_KEY = 'azure-account-key' DEFAULT_AZURE_CONTAINER_NAME = 'azure-container-storage-name' # Internal access token for communicationcation between services via network call -INTERNAL_ACCESS_TOKEN = 'internal-access-token' +INTERNAL_ACCESS_TOKEN = 'internal_access_token' #Enable logging of network request ENABLE_LOG = true @@ -76,7 +76,7 @@ ACCESS_TOKEN_EXPIRY = '1' REFRESH_TOKEN_EXPIRY = '183' # Redis Host connectivity url -REDIS_HOST = 'redis://localhost:6379' +REDIS_HOST = 'redis://redis:6379' # Otp expiration time for forgetpassword or registration process OTP_EXP_TIME = 86400 @@ -115,3 +115,32 @@ IV="2lIctRkqzYMWbwlW1jCC9A==" ERROR_LOG_LEVEL='silly' DISABLE_LOG=false SESSION_MENTEE_LIMIT=0 + +APPLICATION_HOST="localhost" +MENTORING_SERVICE_URL="http://localhost:3000" +PORTAL_URL="PORTAL_URL" +EMAIL_ID_ENCRYPTION_IV="c9c7bd480494409071847264652f5c95" +EMAIL_ID_ENCRYPTION_KEY="eef7e009626c18724be86afa41a2620e0718561a508c61f92d7ee0377177ef7b" +EVENT_ORG_LISTENER_URLS="http://localhost:3000/mentoring/v1/organization/eventListener" +CAPTCHA_ENABLE=false +RECAPTCHA_SECRET_KEY=RECAPTCHA_SECRET_KEY +CLOUD_STORAGE = 'GCP/AWS/AZURE' +CLOUD_STORAGE_ACCOUNTNAME = '****' +CLOUD_STORAGE_BUCKETNAME = '****' +CLOUD_STORAGE_BUCKET_TYPE = '****' +CLOUD_STORAGE_PROJECT = "CLOUD_STORAGE_PROJECT" +CLOUD_STORAGE_PROVIDER = "gcloud" +CLOUD_STORAGE_SECRET = "CLOUD_STORAGE_SECRET" +ENTITY_MANAGEMENT_SERVICE_BASE_URL="http://localhost:3000/mentoring/" +PUBLIC_ASSET_BUCKETNAME=PUBLIC_ASSET_BUCKETNAME +EVENT_USER_KAFKA_TOPIC=dev.userCreate + +SIGNED_URL_EXPIRY_IN_SECONDS="900" +ACCESS_TOKEN_EXPIRY="4320m" +REFRESH_TOKEN_EXPIRY="7" +DEFAULT_ROLE="mentor,mentee" +DEFAULT_TENANT_ORG_CODE="default_code" +DEFAULT_TENANT_ORG_NAME="Default Organization" +DEFAULT_ORG_ID=1 +DEFAULT_ORGANISATION_CODE="default_code" +IS_AUTH_TOKEN_BEARER=false \ No newline at end of file From bbd8fc17e82ba792056d6f7cd144a65eb55ce816 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 10 Dec 2025 23:41:35 +0530 Subject: [PATCH 03/66] jest modification --- src/setupFileAfterEnv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setupFileAfterEnv.js b/src/setupFileAfterEnv.js index b5b616467..cfe3b5040 100644 --- a/src/setupFileAfterEnv.js +++ b/src/setupFileAfterEnv.js @@ -6,7 +6,7 @@ const pool = new Pool() expect.extend(matchers) //PostgreSQL connection string -const connectionString = 'postgres://postgres:postgres@localhost:5432/mentoring-local' +const connectionString = 'postgres://postgres:postgres@localhost:5432/elevate-mentoring' // Connect to the PostgreSQL database using the connection string const db = new Client({ From d45e8061bbbb27a38c936af942141004831ba07f Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 10 Dec 2025 23:54:08 +0530 Subject: [PATCH 04/66] package json modification --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 130dd5923..d36cd0728 100644 --- a/src/package.json +++ b/src/package.json @@ -73,7 +73,7 @@ "@faker-js/faker": "^7.6.0", "eslint": "^8.16.0", "husky": "^8.0.1", - "jest": "^28.1.1", + "jest": "^30.2.0", "jest-json-schema": "^6.1.0", "jest-junit": "^14.0.1", "lint-staged": "^12.4.1", From 0466e7412b189af17b595c4a27b02e78f729fb2e Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 00:50:03 +0530 Subject: [PATCH 05/66] savepoint --- src/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/package.json b/src/package.json index d36cd0728..c508636a8 100644 --- a/src/package.json +++ b/src/package.json @@ -11,6 +11,7 @@ "start": "NODE_ENV=development nodemon app.js", "prod": "NODE_ENV=production node app.js", "stage": "NODE_ENV=stage node app.js", + "dev": "node --inspect=0.0.0.0:9229 app.js", "qa": "NODE_ENV=qa node app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", From d1be292637cfa764261f00f0a62d372ffb6db7eb Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 01:21:16 +0530 Subject: [PATCH 06/66] pushing test sequencer --- src/integration-tests-new/commonTests.js | 2 - src/integration-tests-new/testSequencer.js | 57 ++++++++++++++++++++++ src/integrationJest.config.js | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/integration-tests-new/testSequencer.js diff --git a/src/integration-tests-new/commonTests.js b/src/integration-tests-new/commonTests.js index bc377074e..d2b55c1a5 100644 --- a/src/integration-tests-new/commonTests.js +++ b/src/integration-tests-new/commonTests.js @@ -85,8 +85,6 @@ const mentorLogIn = async () => { secretCode: 'secret-code', }) - console.log(res.body, '<-- 87') - res = await request.post('/user/v1/account/login').set('origin', 'localhost').send({ identifier: email, password: 'PassworD@@@123', diff --git a/src/integration-tests-new/testSequencer.js b/src/integration-tests-new/testSequencer.js new file mode 100644 index 000000000..9930bf115 --- /dev/null +++ b/src/integration-tests-new/testSequencer.js @@ -0,0 +1,57 @@ +const Sequencer = require('@jest/test-sequencer').default +const path = require('path') + +class CustomSequencer extends Sequencer { + sort(tests) { + // Define exact execution order (must match actual filenames) + const executionOrder = [ + 'connections/connections.specs.js', + 'entity/entity.specs.js', + 'entity-type/entity-type.specs.js', + 'form/form.specs.js', + 'mentees/mentees.specs.js', + 'mentors/mentors.specs.js', + 'profile/profile.specs.js', + 'requestSessions/requestSessions.specs.js', + 'sessions/sessions.specs.js', + 'default-rule/default-rule.specs.js', + ] + + // Map tests to their relative paths + const testMap = {} + tests.forEach((test) => { + const relPath = path.relative(path.join(process.cwd(), 'integration-tests-new'), test.path) + testMap[relPath] = test + }) + + // Create ordered test array + const orderedTests = [] + executionOrder.forEach((testPath) => { + if (testMap[testPath]) { + orderedTests.push(testMap[testPath]) + delete testMap[testPath] + } else { + console.warn(`⚠️ Test not found: ${testPath}`) + } + }) + + // Add any remaining tests at the end + const remainingTests = Object.values(testMap) + if (remainingTests.length) { + console.warn( + '⚠️ These tests were not in the execution order:', + remainingTests.map((t) => path.relative(path.join(process.cwd(), 'integration-tests'), t.path)) + ) + orderedTests.push(...remainingTests) + } + + console.log('✅ Final execution order:') + orderedTests.forEach((test, index) => { + console.log(`${index + 1}. ${path.relative(path.join(process.cwd(), 'integration-tests'), test.path)}`) + }) + + return orderedTests + } +} + +module.exports = CustomSequencer diff --git a/src/integrationJest.config.js b/src/integrationJest.config.js index 1c1a2d797..11cc5985e 100644 --- a/src/integrationJest.config.js +++ b/src/integrationJest.config.js @@ -20,6 +20,7 @@ module.exports = { '@utils/(.*)': '/utils/$1', }, testMatch: ['/integration-tests-new/**/*.specs.js'], + testSequencer: '/integration-tests-new/testSequencer', reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: '../dev-ops/report' }]], } /* Add env variables used by jest here because jest do not have access to app or docker env files. From aac18aebf45197fb57e50e1195f6256b941b7e2c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 12:42:54 +0530 Subject: [PATCH 07/66] updates after addressing pr comments --- dev-ops/docker-compose.yml | 10 +-- src/integration-tests-new/admin/admin.spec.js | 53 +++--------- .../cloud-services/cloud-services.spec.js | 22 +---- .../config/config.spec.js | 12 +-- .../feedback/feedback.spec.js | 22 +---- .../issues/issues.spec.js | 12 +-- .../mentees/mentees.specs.js | 9 +- .../mentoring/mentoring.spec.js | 12 +-- .../modules/modules.spec.js | 42 ++-------- .../org-admin/org-admin.spec.js | 82 ++++--------------- .../permissions/permissions.spec.js | 42 ++-------- .../question-set/question-set.spec.js | 32 ++------ .../questions/questions.spec.js | 32 ++------ .../report-mapping/report-mapping.spec.js | 42 ++-------- .../report-queries/report-queries.spec.js | 42 ++-------- .../report-type/report-type.spec.js | 46 +++-------- .../reports/reports.spec.js | 62 +++----------- .../requestSessions/requestSessions.specs.js | 40 ++------- .../role-extension/role-extension.spec.js | 45 ++-------- .../rolePermissionMapping.spec.js | 26 ++---- .../sessions/sessions.specs.js | 70 ++++------------ src/integration-tests-new/users/users.spec.js | 22 +---- 22 files changed, 153 insertions(+), 624 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index a311e7018..9e1dd9ce3 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -52,7 +52,6 @@ services: logging: driver: local - # NEW: Citus DB (Fresh instance, no volumes) citus: image: citusdata/citus:13.0 container_name: citus_db @@ -70,10 +69,8 @@ services: timeout: 5s retries: 5 - # ---------------- APPLICATION SERVICES ---------------- - mentoring: - build: '../' # Preserved from your old file + build: '../' image: elevate/mentoring:2.4 volumes: - ../src/:/var/src @@ -103,7 +100,7 @@ services: - ./integration_test.mentoring.env user: - build: '../../user/' # Preserved from your old file + build: '../../user/' image: elevate/user:2.4 volumes: - ../../user/src/:/var/src @@ -140,5 +137,4 @@ networks: volumes: zookeeper-data: - kafka-data: - # No DB volumes here = Fresh DB every time \ No newline at end of file + kafka-data: \ No newline at end of file diff --git a/src/integration-tests-new/admin/admin.spec.js b/src/integration-tests-new/admin/admin.spec.js index 72a674c9c..d899b5c24 100644 --- a/src/integration-tests-new/admin/admin.spec.js +++ b/src/integration-tests-new/admin/admin.spec.js @@ -1,9 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) - const schemas = require('./schemas/admin.schemas.json') describe('admin endpoints generated from api-doc.yaml', () => { @@ -13,16 +10,10 @@ describe('admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['DELETE_mentoring_v1_admin_userDelete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -32,16 +23,10 @@ describe('admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_admin_triggerViewRebuild'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -51,16 +36,10 @@ describe('admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_admin_triggerViewRebuildInternal'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -70,16 +49,10 @@ describe('admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_admin_triggerPeriodicViewRefresh'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -89,16 +62,10 @@ describe('admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_admin_triggerPeriodicViewRefreshInternal'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/cloud-services/cloud-services.spec.js b/src/integration-tests-new/cloud-services/cloud-services.spec.js index 2c1208f67..770a8ab36 100644 --- a/src/integration-tests-new/cloud-services/cloud-services.spec.js +++ b/src/integration-tests-new/cloud-services/cloud-services.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/cloud-services.schemas.json') @@ -13,16 +11,10 @@ describe('cloud-services endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_cloud-services_getSignedUrl'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -32,16 +24,10 @@ describe('cloud-services endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_cloud-services_getDownloadableUrl'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/config/config.spec.js b/src/integration-tests-new/config/config.spec.js index aea2738b2..9b8d71c28 100644 --- a/src/integration-tests-new/config/config.spec.js +++ b/src/integration-tests-new/config/config.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/config.schemas.json') @@ -12,16 +10,10 @@ describe('config endpoints generated from api-doc.yaml', () => { const url = `/mentoring/v1/config/get` let req = request(BASE).get(url) const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_config_get'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/feedback/feedback.spec.js b/src/integration-tests-new/feedback/feedback.spec.js index 21dd64a34..bcce44b56 100644 --- a/src/integration-tests-new/feedback/feedback.spec.js +++ b/src/integration-tests-new/feedback/feedback.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/feedback.schemas.json') @@ -13,16 +11,10 @@ describe('feedback endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_/mentoring/v1/feedback/forms/{SessionId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -49,16 +41,10 @@ describe('feedback endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_/mentoring/v1/feedback/submit/{SessionId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/issues/issues.spec.js b/src/integration-tests-new/issues/issues.spec.js index c6c42470e..2e2429ba9 100644 --- a/src/integration-tests-new/issues/issues.spec.js +++ b/src/integration-tests-new/issues/issues.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/issues.schemas.json') @@ -23,16 +21,10 @@ describe('issues endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_issues_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/mentees/mentees.specs.js b/src/integration-tests-new/mentees/mentees.specs.js index 98b9a58f8..d3997b0e6 100644 --- a/src/integration-tests-new/mentees/mentees.specs.js +++ b/src/integration-tests-new/mentees/mentees.specs.js @@ -32,8 +32,7 @@ describe('mentees endpoints generated from api-doc.yaml', () => { // let req = request(BASE).get(url); // req = req.set('x-auth-token', "string"); // const res = await req; - // expect(res.status).toBeGreaterThanOrEqual(200); - // expect(res.status).toBeLessThan(300); + // expect(res.status).toBe(200); // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/sessions']); // }); @@ -51,8 +50,7 @@ describe('mentees endpoints generated from api-doc.yaml', () => { // let req = request(BASE).get(url); // req = req.set('x-auth-token', "string"); // const res = await req; - // expect(res.status).toBeGreaterThanOrEqual(200); - // expect(res.status).toBeLessThan(300); + // expect(res.status).toBe(200); // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/joinSession/{sessionId}']); // }); @@ -70,8 +68,7 @@ describe('mentees endpoints generated from api-doc.yaml', () => { // let req = request(BASE).get(url); // req = req.set('x-auth-token', "string"); // const res = await req; - // expect(res.status).toBeGreaterThanOrEqual(200); - // expect(res.status).toBeLessThan(300); + // expect(res.status).toBe(200); // expect(res.body).toMatchSchema(schemas['GET_/mentoring/v1/mentees/reports']); // }); diff --git a/src/integration-tests-new/mentoring/mentoring.spec.js b/src/integration-tests-new/mentoring/mentoring.spec.js index 762d20e45..2728105ed 100644 --- a/src/integration-tests-new/mentoring/mentoring.spec.js +++ b/src/integration-tests-new/mentoring/mentoring.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/mentoring.schemas.json') @@ -12,16 +10,10 @@ describe('mentoring endpoints generated from api-doc.yaml', () => { const url = `/mentoring/health` let req = request(BASE).get(url) const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_health'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/modules/modules.spec.js b/src/integration-tests-new/modules/modules.spec.js index f92690614..12496e575 100644 --- a/src/integration-tests-new/modules/modules.spec.js +++ b/src/integration-tests-new/modules/modules.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/modules.schemas.json') @@ -18,16 +16,10 @@ describe('modules endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_modules_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -51,16 +43,10 @@ describe('modules endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_modules_update_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -79,16 +65,10 @@ describe('modules endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['DELETE_mentoring_v1_modules_delete_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -98,16 +78,10 @@ describe('modules endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_modules_list_page_page_limit_limit_search_search'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/org-admin/org-admin.spec.js b/src/integration-tests-new/org-admin/org-admin.spec.js index 4d64118d9..a192c01d5 100644 --- a/src/integration-tests-new/org-admin/org-admin.spec.js +++ b/src/integration-tests-new/org-admin/org-admin.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/org-admin.schemas.json') @@ -20,16 +18,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_roleChange'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -45,16 +37,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_inheritEntityType'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -64,16 +50,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_org-admin_getOrgPolicies'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -94,16 +74,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_updateRelatedOrgs'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -122,16 +96,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_setOrgPolicies'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -142,16 +110,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { req = req.set('x-auth-token', 'string') req = req.send('string').set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_uploadSampleCSV'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -169,16 +131,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_org-admin_updateTheme'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -188,16 +144,10 @@ describe('org-admin endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_org-admin_themeDetails'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/permissions/permissions.spec.js b/src/integration-tests-new/permissions/permissions.spec.js index 783852a34..e066740e1 100644 --- a/src/integration-tests-new/permissions/permissions.spec.js +++ b/src/integration-tests-new/permissions/permissions.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/permissions.schemas.json') @@ -22,16 +20,10 @@ describe('permissions endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_permissions_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -59,16 +51,10 @@ describe('permissions endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_permissions_update_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -78,16 +64,10 @@ describe('permissions endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['DELETE_mentoring_v1_permissions_delete_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -97,16 +77,10 @@ describe('permissions endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_permissions_list_page_page_limit_limit_search_search'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/question-set/question-set.spec.js b/src/integration-tests-new/question-set/question-set.spec.js index a115154a9..c227c972a 100644 --- a/src/integration-tests-new/question-set/question-set.spec.js +++ b/src/integration-tests-new/question-set/question-set.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/question-set.schemas.json') @@ -19,16 +17,10 @@ describe('question-set endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_/mentoring/v1/question-set/create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -50,16 +42,10 @@ describe('question-set endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['PATCH_/mentoring/v1/question-set/update/{QuestionSetId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -75,16 +61,10 @@ describe('question-set endpoints generated from api-doc.yaml', () => { let req = request(BASE).post(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_/mentoring/v1/question-set/read/{QuestionSetId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/questions/questions.spec.js b/src/integration-tests-new/questions/questions.spec.js index 527e000e7..162069fa9 100644 --- a/src/integration-tests-new/questions/questions.spec.js +++ b/src/integration-tests-new/questions/questions.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/questions.schemas.json') @@ -35,16 +33,10 @@ describe('questions endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_/mentoring/v1/questions/create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -78,16 +70,10 @@ describe('questions endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['PUT_/mentoring/v1/questions/update/{QuestionId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -103,16 +89,10 @@ describe('questions endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_/mentoring/v1/questions/read/{QuestionId}'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/report-mapping/report-mapping.spec.js b/src/integration-tests-new/report-mapping/report-mapping.spec.js index 89a0cad46..01a04f8c3 100644 --- a/src/integration-tests-new/report-mapping/report-mapping.spec.js +++ b/src/integration-tests-new/report-mapping/report-mapping.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/report-mapping.schemas.json') @@ -19,16 +17,10 @@ describe('report-mapping endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_report-mapping_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -47,16 +39,10 @@ describe('report-mapping endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_report-mapping_read'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -72,16 +58,10 @@ describe('report-mapping endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_report-mapping_update'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -91,16 +71,10 @@ describe('report-mapping endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['DELETE_mentoring_v1_report-mapping_delete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/report-queries/report-queries.spec.js b/src/integration-tests-new/report-queries/report-queries.spec.js index 472eae998..169c14bf4 100644 --- a/src/integration-tests-new/report-queries/report-queries.spec.js +++ b/src/integration-tests-new/report-queries/report-queries.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/report-queries.schemas.json') @@ -13,16 +11,10 @@ describe('report-queries endpoints generated from api-doc.yaml', () => { let req = request(BASE).post(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_report-queries_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -41,16 +33,10 @@ describe('report-queries endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_report-queries_read'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -60,16 +46,10 @@ describe('report-queries endpoints generated from api-doc.yaml', () => { let req = request(BASE).post(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_report-queries_update'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -79,16 +59,10 @@ describe('report-queries endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['DELETE_mentoring_v1_report-queries_delete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/report-type/report-type.spec.js b/src/integration-tests-new/report-type/report-type.spec.js index 02d05b1d0..8515f9f8b 100644 --- a/src/integration-tests-new/report-type/report-type.spec.js +++ b/src/integration-tests-new/report-type/report-type.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/report-type.schemas.json') @@ -18,16 +16,10 @@ describe('report-type endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_report-type_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -44,18 +36,12 @@ describe('report-type endpoints generated from api-doc.yaml', () => { test('should return 200', async () => { const url = `/mentoring/v1/report-type/read?title=random_title` let req = request(BASE).get(url) - req = req.set('x-auth-token', 'bearer {{token}}') + req = req.set('x-auth-token', TOKEN) const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_report-type_read'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -63,23 +49,17 @@ describe('report-type endpoints generated from api-doc.yaml', () => { test('should return 200', async () => { const url = `/mentoring/v1/report-type/update?id=1` let req = request(BASE).post(url) - req = req.set('x-auth-token', 'bearer {{token}}') + req = req.set('x-auth-token', TOKEN) req = req .send({ title: 'string', }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['POST_mentoring_v1_report-type_update'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -89,16 +69,10 @@ describe('report-type endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['DELETE_mentoring_v1_report-type_delete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/reports/reports.spec.js b/src/integration-tests-new/reports/reports.spec.js index c6f0acb02..cb89a50a3 100644 --- a/src/integration-tests-new/reports/reports.spec.js +++ b/src/integration-tests-new/reports/reports.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/reports.schemas.json') @@ -45,16 +43,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_reports_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -73,16 +65,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['GET_mentoring_v1_reports_read'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -102,16 +88,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['POST_mentoring_v1_reports_update_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -121,16 +101,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(202) // validate response schema const schema = schemas['DELETE_mentoring_v1_reports_delete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -150,16 +124,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_reports_reportData'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -169,16 +137,10 @@ describe('reports endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_reports_filterList'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) diff --git a/src/integration-tests-new/requestSessions/requestSessions.specs.js b/src/integration-tests-new/requestSessions/requestSessions.specs.js index 7be33efb9..42c701888 100644 --- a/src/integration-tests-new/requestSessions/requestSessions.specs.js +++ b/src/integration-tests-new/requestSessions/requestSessions.specs.js @@ -194,16 +194,10 @@ describe('Standalone requestSessions endpoints', () => { let req = request(BASE).get(url); req = req.set('x-auth-token', "test-token"); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['GET_mentoring_v1_requestSessions_getDetails']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -220,16 +214,10 @@ describe('Standalone requestSessions endpoints', () => { let req = request(BASE).get(url); req = req.set('x-auth-token', "test-token"); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['GET_mentoring_v1_requestSessions_userAvailability']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -249,16 +237,10 @@ describe('Standalone requestSessions endpoints', () => { "request_session_id": "string" }).set('Content-Type', 'application/json'); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(201); // validate response schema const schema = schemas['POST_mentoring_v1_requestSessions_accept']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -287,16 +269,10 @@ describe('Standalone requestSessions endpoints', () => { "request_session_id": "string" }).set('Content-Type', 'application/json'); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(201); // validate response schema const schema = schemas['POST_mentoring_v1_requestSessions_reject']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/role-extension/role-extension.spec.js b/src/integration-tests-new/role-extension/role-extension.spec.js index b86d14330..8663bf67e 100644 --- a/src/integration-tests-new/role-extension/role-extension.spec.js +++ b/src/integration-tests-new/role-extension/role-extension.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/role-extension.schemas.json') @@ -22,16 +20,10 @@ describe('role-extension endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_role-extension_create'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { @@ -57,16 +49,10 @@ describe('role-extension endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'bearer {{token}}') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_role-extension_read'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -83,16 +69,10 @@ describe('role-extension endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_role-extension_update'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -109,19 +89,12 @@ describe('role-extension endpoints generated from api-doc.yaml', () => { test('should return 200', async () => { const url = `/mentoring/v1/role-extension/delete?title=mentee` let req = request(BASE).delete(url) - req = req.set('x-auth-token', 'bearer {{token}}') - req = req.set('x-auth-token', 'test-token') + req = req.set('x-auth-token', TOKEN) const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['DELETE_mentoring_v1_role-extension_delete'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js index b494c3e3e..d3327a15a 100644 --- a/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js +++ b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/rolePermissionMapping.schemas.json') @@ -18,16 +16,10 @@ describe('rolePermissionMapping endpoints generated from api-doc.yaml', () => { }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_rolePermissionMapping_create_role_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { @@ -44,29 +36,23 @@ describe('rolePermissionMapping endpoints generated from api-doc.yaml', () => { test('should return 201', async () => { const url = `/mentoring/v1/rolePermissionMapping/delete/{role_id}` let req = request(BASE).post(url) - req = req.set('x-auth-token', 'string') + req = req.set('x-auth-token', TOKEN) req = req .send({ permission_id: 1, }) .set('Content-Type', 'application/json') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(201) // validate response schema const schema = schemas['POST_mentoring_v1_rolePermissionMapping_delete_role_id'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) test('should return 400/422 for invalid body', async () => { const url = `/mentoring/v1/rolePermissionMapping/delete/{role_id}` let req = request(BASE).post(url) - req = req.set('x-auth-token', 'string') + req = req.set('x-auth-token', TOKEN) req = req.send({}).set('Content-Type', 'application/json') const res = await req expect([400, 422]).toContain(res.status) diff --git a/src/integration-tests-new/sessions/sessions.specs.js b/src/integration-tests-new/sessions/sessions.specs.js index af8061b99..3bc4b9d41 100644 --- a/src/integration-tests-new/sessions/sessions.specs.js +++ b/src/integration-tests-new/sessions/sessions.specs.js @@ -368,16 +368,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url); req = req.set('x-auth-token', userDetails.token); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['GET_/mentoring/v1/sessions/list']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -395,16 +389,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url); req = req.set('x-auth-token', userDetails.token); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['GET_/mentoring/v1/sessions/share/{sessionId}']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -444,16 +432,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { ] }).set('Content-Type', 'application/json'); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(201); // validate response schema const schema = schemas['POST_/mentoring/v1/sessions/update']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -471,16 +453,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { let req = request(BASE).delete(url); req = req.set('x-auth-token', userDetails.token); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(202); // validate response schema const schema = schemas['DELETE_/mentoring/v1/sessions/update/{sessionId}']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -518,16 +494,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { ] }).set('Content-Type', 'application/json'); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(202); // validate response schema const schema = schemas['POST_/mentoring/v1/sessions/update/{sessionId}']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -545,16 +515,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url); req = req.set('x-auth-token', userDetails.token); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['GET_/mentoring/v1/sessions/getRecording/{sessionId}']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { @@ -572,16 +536,10 @@ describe('sessions endpoints generated from api-doc.yaml', () => { let req = request(BASE).patch(url); req = req.set('x-auth-token', userDetails.token); const res = await req; - expect(res.status).toBeGreaterThanOrEqual(200); - expect(res.status).toBeLessThan(300); + expect(res.status).toBe(200); // validate response schema const schema = schemas['PATCH_/mentoring/v1/sessions/updateRecordingUrl/{internalSessionId}']; - const validate = ajv.compile(schema); - const valid = validate(res.body); - if (!valid) { - console.error("Schema validation errors:", validate.errors); - } - expect(valid).toBe(true); + expect(res.body).toMatchSchema(schema); }); test('should return 401/403 when unauthorized', async () => { diff --git a/src/integration-tests-new/users/users.spec.js b/src/integration-tests-new/users/users.spec.js index 5bfb1cfa7..8150cc16d 100644 --- a/src/integration-tests-new/users/users.spec.js +++ b/src/integration-tests-new/users/users.spec.js @@ -1,8 +1,6 @@ const request = require('supertest') -const Ajv = require('ajv') const BASE = process.env.BASE_URL || 'http://localhost:3000' const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' -const ajv = new Ajv({ strict: false }) const schemas = require('./schemas/users.schemas.json') @@ -13,16 +11,10 @@ describe('users endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_users_pendingFeedbacks'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) @@ -32,16 +24,10 @@ describe('users endpoints generated from api-doc.yaml', () => { let req = request(BASE).get(url) req = req.set('x-auth-token', 'string') const res = await req - expect(res.status).toBeGreaterThanOrEqual(200) - expect(res.status).toBeLessThan(300) + expect(res.status).toBe(200) // validate response schema const schema = schemas['GET_mentoring_v1_users_list_type_userType_page_page_limit_limit_search_search'] - const validate = ajv.compile(schema) - const valid = validate(res.body) - if (!valid) { - console.error('Schema validation errors:', validate.errors) - } - expect(valid).toBe(true) + expect(res.body).toMatchSchema(schema) }) }) }) From b92293c50e1a3d467a717368b2e3632aa06330bf Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 14:34:03 +0530 Subject: [PATCH 08/66] kafka file update --- src/configs/kafka.js | 157 +------------------------------------------ 1 file changed, 3 insertions(+), 154 deletions(-) diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 1a5645b7d..67b58c3e1 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -42,33 +42,15 @@ module.exports = async () => { ) } -/* async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID, - sessionTimeout: 45000, - heartbeatInterval: 3000, - },) - - // ADD HERE — consumer lifecycle debug logs - consumer.on(consumer.events.HEARTBEAT, () => { - console.log("Kafka Consumer: Heartbeat OK"); - }); - - consumer.on(consumer.events.REBALANCING, (e) => { - console.log(`Kafka Consumer: Rebalancing (messages may be missed) | reason=${e.payload.reason}`); - }); - - consumer.on(consumer.events.GROUP_JOIN, (e) => { - console.log(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload.memberAssignment)}`); - }); + const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID }) await consumer.connect() await consumer.subscribe({ topics: [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) await consumer.run({ - eachMessage: async ({ topic, partition, message, heartbeat }) => { + eachMessage: async ({ topic, partition, message }) => { try { - console.log({ topic, partition, message , heartbeat},'<---{ topic, partition, message }') const rawValue = message.value?.toString() const offset = message.offset if (!rawValue) { @@ -95,7 +77,7 @@ async function startConsumer(kafkaClient) { response = await rolechangeConsumer.messageReceived(payload) } if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload,heartbeat) + response = await createuserConsumer.messageReceived(payload) } if (payload.eventType === 'delete') { response = await deleteuserConsumer.messageReceived(payload) @@ -120,7 +102,6 @@ async function startConsumer(kafkaClient) { } logger.info(`Kafk event handling response : ${response}`) } catch (err) { - console.log(err,'err 106') logger.error(`Error in Kafka message handler for topic ${topic}`, { topic, partition, @@ -131,135 +112,3 @@ async function startConsumer(kafkaClient) { }, }) } - -*/ - -async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ - groupId: process.env.KAFKA_GROUP_ID, - sessionTimeout: 45000, // allows slow handlers - heartbeatInterval: 3000, - }) - - // Lifecycle logs (important) - consumer.on(consumer.events.GROUP_JOIN, (e) => { - logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) - }) - - consumer.on(consumer.events.REBALANCING, (e) => { - logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) - }) - - consumer.on(consumer.events.HEARTBEAT, () => { - logger.debug('Kafka Consumer: Heartbeat OK') - }) - - await consumer.connect() - logger.info('Kafka Consumer: Connected to broker') - - const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] - - await consumer.subscribe({ topics }) - logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) - - //------------------------------------------------------------------ - // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) - //------------------------------------------------------------------ - await consumer.run({ - autoCommit: true, // safe because processing is fast per message - eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { - logger.info( - `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` - ) - - for (const message of batch.messages) { - if (!isRunning() || isStale()) { - logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') - break - } - - const rawValue = message.value?.toString() - const offset = message.offset - const topic = batch.topic - const partition = batch.partition - - logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) - - if (!rawValue) { - logger.warn(`Kafka Batch: Empty message skipped`) - resolveOffset(offset) - continue - } - - let payload - try { - payload = JSON.parse(rawValue) - } catch (e) { - logger.warn('Kafka Batch: Invalid JSON, skipping', { - offset, - err: e.message, - }) - resolveOffset(offset) - continue - } - - //-------------------------------------------------------- - // ROUTE MESSAGE TO CORRECT HANDLER - //-------------------------------------------------------- - let response - - try { - if (topic === process.env.EVENTS_TOPIC && payload) { - if (payload.eventType === 'roleChange') { - response = await rolechangeConsumer.messageReceived(payload) - } - - if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'delete') { - response = await deleteuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { - response = await updateuserConsumer.messageReceived(payload) - } - - // organization events - if ( - payload.entity === 'organization' && - (payload.eventType === 'create' || - payload.eventType === 'update' || - payload.eventType === 'deactivate') - ) { - response = await organizationConsumer.messageReceived(payload) - } - } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { - response = await utils.internalDel(payload.value) - } - - logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) - } catch (handlerErr) { - logger.error(`Kafka Batch: Error handling message`, { - offset, - err: handlerErr.stack || handlerErr.message, - }) - } - - //-------------------------------------------------------- - // MARK MESSAGE AS PROCESSED - //-------------------------------------------------------- - resolveOffset(offset) - - //-------------------------------------------------------- - // HEARTBEAT TO PREVENT TIMEOUT - //-------------------------------------------------------- - await heartbeat() - } - - // commit offsets once per batch - await commitOffsetsIfNecessary() - }, - }) -} From 6998e8e3851947d329372fca37e23e62bf2dc76b Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 14:47:09 +0530 Subject: [PATCH 09/66] kafka change --- src/configs/kafka.js | 157 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 3 deletions(-) diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 67b58c3e1..1a5645b7d 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -42,15 +42,33 @@ module.exports = async () => { ) } +/* async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID }) + const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, + heartbeatInterval: 3000, + },) + + // ADD HERE — consumer lifecycle debug logs + consumer.on(consumer.events.HEARTBEAT, () => { + console.log("Kafka Consumer: Heartbeat OK"); + }); + + consumer.on(consumer.events.REBALANCING, (e) => { + console.log(`Kafka Consumer: Rebalancing (messages may be missed) | reason=${e.payload.reason}`); + }); + + consumer.on(consumer.events.GROUP_JOIN, (e) => { + console.log(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload.memberAssignment)}`); + }); await consumer.connect() await consumer.subscribe({ topics: [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) await consumer.run({ - eachMessage: async ({ topic, partition, message }) => { + eachMessage: async ({ topic, partition, message, heartbeat }) => { try { + console.log({ topic, partition, message , heartbeat},'<---{ topic, partition, message }') const rawValue = message.value?.toString() const offset = message.offset if (!rawValue) { @@ -77,7 +95,7 @@ async function startConsumer(kafkaClient) { response = await rolechangeConsumer.messageReceived(payload) } if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) + response = await createuserConsumer.messageReceived(payload,heartbeat) } if (payload.eventType === 'delete') { response = await deleteuserConsumer.messageReceived(payload) @@ -102,6 +120,7 @@ async function startConsumer(kafkaClient) { } logger.info(`Kafk event handling response : ${response}`) } catch (err) { + console.log(err,'err 106') logger.error(`Error in Kafka message handler for topic ${topic}`, { topic, partition, @@ -112,3 +131,135 @@ async function startConsumer(kafkaClient) { }, }) } + +*/ + +async function startConsumer(kafkaClient) { + const consumer = kafkaClient.consumer({ + groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, // allows slow handlers + heartbeatInterval: 3000, + }) + + // Lifecycle logs (important) + consumer.on(consumer.events.GROUP_JOIN, (e) => { + logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) + }) + + consumer.on(consumer.events.REBALANCING, (e) => { + logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) + }) + + consumer.on(consumer.events.HEARTBEAT, () => { + logger.debug('Kafka Consumer: Heartbeat OK') + }) + + await consumer.connect() + logger.info('Kafka Consumer: Connected to broker') + + const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] + + await consumer.subscribe({ topics }) + logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) + + //------------------------------------------------------------------ + // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) + //------------------------------------------------------------------ + await consumer.run({ + autoCommit: true, // safe because processing is fast per message + eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { + logger.info( + `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` + ) + + for (const message of batch.messages) { + if (!isRunning() || isStale()) { + logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') + break + } + + const rawValue = message.value?.toString() + const offset = message.offset + const topic = batch.topic + const partition = batch.partition + + logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) + + if (!rawValue) { + logger.warn(`Kafka Batch: Empty message skipped`) + resolveOffset(offset) + continue + } + + let payload + try { + payload = JSON.parse(rawValue) + } catch (e) { + logger.warn('Kafka Batch: Invalid JSON, skipping', { + offset, + err: e.message, + }) + resolveOffset(offset) + continue + } + + //-------------------------------------------------------- + // ROUTE MESSAGE TO CORRECT HANDLER + //-------------------------------------------------------- + let response + + try { + if (topic === process.env.EVENTS_TOPIC && payload) { + if (payload.eventType === 'roleChange') { + response = await rolechangeConsumer.messageReceived(payload) + } + + if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { + response = await createuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'delete') { + response = await deleteuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { + response = await updateuserConsumer.messageReceived(payload) + } + + // organization events + if ( + payload.entity === 'organization' && + (payload.eventType === 'create' || + payload.eventType === 'update' || + payload.eventType === 'deactivate') + ) { + response = await organizationConsumer.messageReceived(payload) + } + } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { + response = await utils.internalDel(payload.value) + } + + logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) + } catch (handlerErr) { + logger.error(`Kafka Batch: Error handling message`, { + offset, + err: handlerErr.stack || handlerErr.message, + }) + } + + //-------------------------------------------------------- + // MARK MESSAGE AS PROCESSED + //-------------------------------------------------------- + resolveOffset(offset) + + //-------------------------------------------------------- + // HEARTBEAT TO PREVENT TIMEOUT + //-------------------------------------------------------- + await heartbeat() + } + + // commit offsets once per batch + await commitOffsetsIfNecessary() + }, + }) +} From 821d638c78f1b063ef61a1ffe1599ae84f522991 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 15:03:58 +0530 Subject: [PATCH 10/66] feat:updated docker compose with kafka circleci file --- dev-ops/docker-compose.yml | 1 + src/configs/kafka.ci.js | 173 +++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/configs/kafka.ci.js diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 9e1dd9ce3..dbf7038d0 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -74,6 +74,7 @@ services: image: elevate/mentoring:2.4 volumes: - ../src/:/var/src + - ./configs/kafka.ci.js:/var/src/configs/kafka.js:ro ports: - '3000:3000' - '9229:9229' diff --git a/src/configs/kafka.ci.js b/src/configs/kafka.ci.js new file mode 100644 index 000000000..b0e59bc4d --- /dev/null +++ b/src/configs/kafka.ci.js @@ -0,0 +1,173 @@ +/** + * name : configs/kafka + * author : Aman Gupta + * Date : 07-Dec-2021 + * Description : Kafka connection configurations + */ + +const utils = require('@generics/utils') +const { elevateLog } = require('elevate-logger') +const logger = elevateLog.init() +const { Kafka } = require('kafkajs') +const deleteuserConsumer = require('@generics/kafka/consumers/deleteuser') +const rolechangeConsumer = require('@generics/kafka/consumers/rolechange') +const createuserConsumer = require('@generics/kafka/consumers/createuser') +const updateuserConsumer = require('@generics/kafka/consumers/updateuser') +const organizationConsumer = require('@generics/kafka/consumers/organization') + +module.exports = async () => { + const kafkaIps = process.env.KAFKA_URL.split(',') + const KafkaClient = new Kafka({ + clientId: 'mentoring', + brokers: kafkaIps, + }) + + const producer = KafkaClient.producer() + await producer.connect() + + producer.on('producer.connect', () => { + logger.info('KafkaProvider: connected') + }) + producer.on('producer.disconnect', () => { + logger.error('KafkaProvider: could not connect', { + triggerNotification: true, + }) + }) + + global.kafkaProducer = producer + global.kafkaClient = KafkaClient + + startConsumer(KafkaClient).catch((err) => + logger.error('Kafka consumer failed to start', { err: err?.stack || err?.message }) + ) +} + +async function startConsumer(kafkaClient) { + const consumer = kafkaClient.consumer({ + groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, // allows slow handlers + heartbeatInterval: 3000, + }) + + // Lifecycle logs (important) + consumer.on(consumer.events.GROUP_JOIN, (e) => { + logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) + }) + + consumer.on(consumer.events.REBALANCING, (e) => { + logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) + }) + + consumer.on(consumer.events.HEARTBEAT, () => { + logger.debug('Kafka Consumer: Heartbeat OK') + }) + + await consumer.connect() + logger.info('Kafka Consumer: Connected to broker') + + const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] + + await consumer.subscribe({ topics }) + logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) + + //------------------------------------------------------------------ + // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) + //------------------------------------------------------------------ + await consumer.run({ + autoCommit: true, // safe because processing is fast per message + eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { + logger.info( + `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` + ) + + for (const message of batch.messages) { + if (!isRunning() || isStale()) { + logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') + break + } + + const rawValue = message.value?.toString() + const offset = message.offset + const topic = batch.topic + const partition = batch.partition + + logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) + + if (!rawValue) { + logger.warn(`Kafka Batch: Empty message skipped`) + resolveOffset(offset) + continue + } + + let payload + try { + payload = JSON.parse(rawValue) + } catch (e) { + logger.warn('Kafka Batch: Invalid JSON, skipping', { + offset, + err: e.message, + }) + resolveOffset(offset) + continue + } + + //-------------------------------------------------------- + // ROUTE MESSAGE TO CORRECT HANDLER + //-------------------------------------------------------- + let response + + try { + if (topic === process.env.EVENTS_TOPIC && payload) { + if (payload.eventType === 'roleChange') { + response = await rolechangeConsumer.messageReceived(payload) + } + + if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { + response = await createuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'delete') { + response = await deleteuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { + response = await updateuserConsumer.messageReceived(payload) + } + + // organization events + if ( + payload.entity === 'organization' && + (payload.eventType === 'create' || + payload.eventType === 'update' || + payload.eventType === 'deactivate') + ) { + response = await organizationConsumer.messageReceived(payload) + } + } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { + response = await utils.internalDel(payload.value) + } + + logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) + } catch (handlerErr) { + logger.error(`Kafka Batch: Error handling message`, { + offset, + err: handlerErr.stack || handlerErr.message, + }) + } + + //-------------------------------------------------------- + // MARK MESSAGE AS PROCESSED + //-------------------------------------------------------- + resolveOffset(offset) + + //-------------------------------------------------------- + // HEARTBEAT TO PREVENT TIMEOUT + //-------------------------------------------------------- + await heartbeat() + } + + // commit offsets once per batch + await commitOffsetsIfNecessary() + }, + }) +} From aa0e9b36f8b2dcab84961b2d42daeac0a3bc60da Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 15:06:44 +0530 Subject: [PATCH 11/66] feat:updated pakcage json and docker compose file --- dev-ops/docker-compose.yml | 4 ++-- src/package.json | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index dbf7038d0..f4761593a 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -110,8 +110,8 @@ services: # - '9229:9229' command: > sh -c "sleep 20 && - npm run db:init && - npm run db:seed:all && + npm run db:init:integration && + npm run db:seed:all:integration && nodemon --trace-warnings app.js" environment: - KAFKA_URL=kafka:9092 diff --git a/src/package.json b/src/package.json index c508636a8..1f7639eff 100644 --- a/src/package.json +++ b/src/package.json @@ -15,10 +15,10 @@ "qa": "NODE_ENV=qa node app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", - "db:init:old": "(sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check') && sequelize-cli db:migrate", - "db:init": "sequelize-cli db:create || echo 'exists'; node scripts/run-migrations.js", - "db:seed:all:old": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' ", - "db:seed:all": "node scripts/run-seeders.js" + "db:init": "(sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check') && sequelize-cli db:migrate", + "db:init:integration": "sequelize-cli db:create || echo 'exists'; node scripts/run-migrations.js", + "db:seed:all": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' ", + "db:seed:all:integration": "node scripts/run-seeders.js" }, "author": "Aman Kumar Gupta ", "license": "ISC", From 4ccdbebd6825147c4ac0a7ef086495a22769133a Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 11 Dec 2025 15:47:22 +0530 Subject: [PATCH 12/66] script for migration script --- dev-ops/docker-compose.yml | 5 ++--- src/start.sh | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index f4761593a..9e1dd9ce3 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -74,7 +74,6 @@ services: image: elevate/mentoring:2.4 volumes: - ../src/:/var/src - - ./configs/kafka.ci.js:/var/src/configs/kafka.js:ro ports: - '3000:3000' - '9229:9229' @@ -110,8 +109,8 @@ services: # - '9229:9229' command: > sh -c "sleep 20 && - npm run db:init:integration && - npm run db:seed:all:integration && + npm run db:init && + npm run db:seed:all && nodemon --trace-warnings app.js" environment: - KAFKA_URL=kafka:9092 diff --git a/src/start.sh b/src/start.sh index 2830e1528..cc4019deb 100755 --- a/src/start.sh +++ b/src/start.sh @@ -1,11 +1,14 @@ #!/bin/sh set -e +# Replace kafka.js with kafka.ci.js +cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js + echo "[MENTORING] Running migrations..." -npm run db:init +npm run db:init:integration echo "[MENTORING] Running seeds..." -npm run db:seed:all +npm run db:seed:all:integration echo "[MENTORING] Starting application..." npm run dev From a78fc1c95f9cf332ee40197974ef2d5cd0a276e4 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 18 Dec 2025 14:18:17 +0530 Subject: [PATCH 13/66] migration script fix --- .../migrations/20230612024757-create-entity-types.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/database/migrations/20230612024757-create-entity-types.js b/src/database/migrations/20230612024757-create-entity-types.js index 8cc391dec..768cca9ec 100644 --- a/src/database/migrations/20230612024757-create-entity-types.js +++ b/src/database/migrations/20230612024757-create-entity-types.js @@ -59,6 +59,16 @@ module.exports = { deleted_at: { type: Sequelize.DATE, }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: process.env.DEFAULT_TENANT_CODE, + }, + organization_code: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: process.env.DEFAULT_ORGANIZATION_CODE, + }, }) // Add an index for the 'value' column From 209c306d010dc992f03317a52a1af2bc94fb9e39 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 22 Dec 2025 14:48:01 +0530 Subject: [PATCH 14/66] restored prev migrations and seeders --- src/configs/kafka.js | 157 +----------------- .../20230611225246-create-sessions.js | 4 - .../20230612024757-create-entity-types.js | 10 -- ...612030911-create-organisation-extension.js | 5 - .../migrations/20230704103100-create-forms.js | 10 -- ...session-columns-and-update-mentor-names.js | 6 +- ...20240122130739-add-managersSession-form.js | 2 - ...20240716111210-update-user-id-to-string.js | 8 +- ...208075619-fix-organization-extension-pk.js | 120 ------------- ...4559-create-organization-extension-data.js | 45 ----- ...822124704-add_entity_types_and_entities.js | 3 - .../seeders/20231103090632-seed-forms.js | 6 - 12 files changed, 8 insertions(+), 368 deletions(-) delete mode 100644 src/database/migrations/20251208075619-fix-organization-extension-pk.js delete mode 100644 src/database/migrations/20251208154559-create-organization-extension-data.js diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 1a5645b7d..67b58c3e1 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -42,33 +42,15 @@ module.exports = async () => { ) } -/* async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID, - sessionTimeout: 45000, - heartbeatInterval: 3000, - },) - - // ADD HERE — consumer lifecycle debug logs - consumer.on(consumer.events.HEARTBEAT, () => { - console.log("Kafka Consumer: Heartbeat OK"); - }); - - consumer.on(consumer.events.REBALANCING, (e) => { - console.log(`Kafka Consumer: Rebalancing (messages may be missed) | reason=${e.payload.reason}`); - }); - - consumer.on(consumer.events.GROUP_JOIN, (e) => { - console.log(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload.memberAssignment)}`); - }); + const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID }) await consumer.connect() await consumer.subscribe({ topics: [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) await consumer.run({ - eachMessage: async ({ topic, partition, message, heartbeat }) => { + eachMessage: async ({ topic, partition, message }) => { try { - console.log({ topic, partition, message , heartbeat},'<---{ topic, partition, message }') const rawValue = message.value?.toString() const offset = message.offset if (!rawValue) { @@ -95,7 +77,7 @@ async function startConsumer(kafkaClient) { response = await rolechangeConsumer.messageReceived(payload) } if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload,heartbeat) + response = await createuserConsumer.messageReceived(payload) } if (payload.eventType === 'delete') { response = await deleteuserConsumer.messageReceived(payload) @@ -120,7 +102,6 @@ async function startConsumer(kafkaClient) { } logger.info(`Kafk event handling response : ${response}`) } catch (err) { - console.log(err,'err 106') logger.error(`Error in Kafka message handler for topic ${topic}`, { topic, partition, @@ -131,135 +112,3 @@ async function startConsumer(kafkaClient) { }, }) } - -*/ - -async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ - groupId: process.env.KAFKA_GROUP_ID, - sessionTimeout: 45000, // allows slow handlers - heartbeatInterval: 3000, - }) - - // Lifecycle logs (important) - consumer.on(consumer.events.GROUP_JOIN, (e) => { - logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) - }) - - consumer.on(consumer.events.REBALANCING, (e) => { - logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) - }) - - consumer.on(consumer.events.HEARTBEAT, () => { - logger.debug('Kafka Consumer: Heartbeat OK') - }) - - await consumer.connect() - logger.info('Kafka Consumer: Connected to broker') - - const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] - - await consumer.subscribe({ topics }) - logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) - - //------------------------------------------------------------------ - // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) - //------------------------------------------------------------------ - await consumer.run({ - autoCommit: true, // safe because processing is fast per message - eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { - logger.info( - `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` - ) - - for (const message of batch.messages) { - if (!isRunning() || isStale()) { - logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') - break - } - - const rawValue = message.value?.toString() - const offset = message.offset - const topic = batch.topic - const partition = batch.partition - - logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) - - if (!rawValue) { - logger.warn(`Kafka Batch: Empty message skipped`) - resolveOffset(offset) - continue - } - - let payload - try { - payload = JSON.parse(rawValue) - } catch (e) { - logger.warn('Kafka Batch: Invalid JSON, skipping', { - offset, - err: e.message, - }) - resolveOffset(offset) - continue - } - - //-------------------------------------------------------- - // ROUTE MESSAGE TO CORRECT HANDLER - //-------------------------------------------------------- - let response - - try { - if (topic === process.env.EVENTS_TOPIC && payload) { - if (payload.eventType === 'roleChange') { - response = await rolechangeConsumer.messageReceived(payload) - } - - if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'delete') { - response = await deleteuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { - response = await updateuserConsumer.messageReceived(payload) - } - - // organization events - if ( - payload.entity === 'organization' && - (payload.eventType === 'create' || - payload.eventType === 'update' || - payload.eventType === 'deactivate') - ) { - response = await organizationConsumer.messageReceived(payload) - } - } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { - response = await utils.internalDel(payload.value) - } - - logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) - } catch (handlerErr) { - logger.error(`Kafka Batch: Error handling message`, { - offset, - err: handlerErr.stack || handlerErr.message, - }) - } - - //-------------------------------------------------------- - // MARK MESSAGE AS PROCESSED - //-------------------------------------------------------- - resolveOffset(offset) - - //-------------------------------------------------------- - // HEARTBEAT TO PREVENT TIMEOUT - //-------------------------------------------------------- - await heartbeat() - } - - // commit offsets once per batch - await commitOffsetsIfNecessary() - }, - }) -} diff --git a/src/database/migrations/20230611225246-create-sessions.js b/src/database/migrations/20230611225246-create-sessions.js index cea6769a0..5ccc11d3d 100644 --- a/src/database/migrations/20230611225246-create-sessions.js +++ b/src/database/migrations/20230611225246-create-sessions.js @@ -123,10 +123,6 @@ module.exports = { custom_entity_text: { type: Sequelize.JSON, }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20230612024757-create-entity-types.js b/src/database/migrations/20230612024757-create-entity-types.js index 768cca9ec..8cc391dec 100644 --- a/src/database/migrations/20230612024757-create-entity-types.js +++ b/src/database/migrations/20230612024757-create-entity-types.js @@ -59,16 +59,6 @@ module.exports = { deleted_at: { type: Sequelize.DATE, }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: process.env.DEFAULT_TENANT_CODE, - }, - organization_code: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: process.env.DEFAULT_ORGANIZATION_CODE, - }, }) // Add an index for the 'value' column diff --git a/src/database/migrations/20230612030911-create-organisation-extension.js b/src/database/migrations/20230612030911-create-organisation-extension.js index 86720761f..e4f6fd6ed 100644 --- a/src/database/migrations/20230612030911-create-organisation-extension.js +++ b/src/database/migrations/20230612030911-create-organisation-extension.js @@ -9,11 +9,6 @@ module.exports = { primaryKey: true, type: Sequelize.INTEGER, }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - primaryKey: true, - }, session_visibility_policy: { type: Sequelize.STRING, }, diff --git a/src/database/migrations/20230704103100-create-forms.js b/src/database/migrations/20230704103100-create-forms.js index efcaf1db1..b5fa05330 100644 --- a/src/database/migrations/20230704103100-create-forms.js +++ b/src/database/migrations/20230704103100-create-forms.js @@ -39,16 +39,6 @@ module.exports = { allowNull: false, primaryKey: true, }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: process.env.DEFAULT_TENANT_CODE, - }, - organization_code: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: process.env.DEFAULT_ORGANIZATION_CODE, - }, }) }, diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index 71a53aa7c..d451d8f41 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,11 +33,7 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const defaultTenantCode = process.env.DEFAULT_TENANT_CODE || 'default' - const sessionsWithNullMentorName = await sessionQueries.findAll( - { mentor_name: 'Mentor' }, - defaultTenantCode - ) + const sessionsWithNullMentorName = await sessionQueries.findAll({ mentor_name: 'Mentor' }) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') diff --git a/src/database/migrations/20240122130739-add-managersSession-form.js b/src/database/migrations/20240122130739-add-managersSession-form.js index 45c9fb1ee..12f995fcb 100644 --- a/src/database/migrations/20240122130739-add-managersSession-form.js +++ b/src/database/migrations/20240122130739-add-managersSession-form.js @@ -337,8 +337,6 @@ module.exports = { organization_id: defaultOrgId, updated_at: new Date(), created_at: new Date(), - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, }, ], {} diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8baafbae6..8aaa64c97 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - // await queryInterface.changeColumn('session_ownerships', 'user_id', { - // type: Sequelize.STRING, - // allowNull: false, - // }) + await queryInterface.changeColumn('session_ownerships', 'user_id', { + type: Sequelize.STRING, + allowNull: false, + }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251208075619-fix-organization-extension-pk.js b/src/database/migrations/20251208075619-fix-organization-extension-pk.js deleted file mode 100644 index bd0e82a4d..000000000 --- a/src/database/migrations/20251208075619-fix-organization-extension-pk.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict' - -module.exports = { - async up(queryInterface, Sequelize) { - const table = 'organization_extension' - - // - // 1. Ensure new columns exist (safety – avoids failures in older DBs) - // - const ensureColumn = async (column, type) => { - const exists = await queryInterface.sequelize.query( - ` - SELECT EXISTS ( - SELECT 1 FROM information_schema.columns - WHERE table_name = '${table}' AND column_name = '${column}' - ) as exists; - `, - { type: Sequelize.QueryTypes.SELECT } - ) - - if (!exists[0].exists) { - await queryInterface.addColumn(table, column, { type, allowNull: true }) - } - } - - await ensureColumn('organization_code', Sequelize.STRING) - await ensureColumn('tenant_code', Sequelize.STRING) - - // - // 2. Backfill NULL values (required before NOT NULL constraint) - // - await queryInterface.sequelize.query( - ` - UPDATE ${table} - SET organization_code = 'default_org' - WHERE organization_code IS NULL; - ` - ) - - await queryInterface.sequelize.query( - ` - UPDATE ${table} - SET tenant_code = 'default_tenant' - WHERE tenant_code IS NULL; - ` - ) - - // - // 3. Apply NOT NULL constraints - // - await queryInterface.changeColumn(table, 'organization_code', { - type: Sequelize.STRING, - allowNull: false, - }) - - await queryInterface.changeColumn(table, 'tenant_code', { - type: Sequelize.STRING, - allowNull: false, - }) - - await queryInterface.changeColumn(table, 'organization_id', { - type: Sequelize.STRING, - allowNull: false, - }) - - // - // 4. Drop existing primary key (if exists) - // - const pkNameQuery = await queryInterface.sequelize.query( - ` - SELECT constraint_name - FROM information_schema.table_constraints - WHERE table_name='${table}' AND constraint_type='PRIMARY KEY'; - `, - { type: Sequelize.QueryTypes.SELECT } - ) - - if (pkNameQuery.length > 0) { - const pkName = pkNameQuery[0].constraint_name - await queryInterface.sequelize.query(`ALTER TABLE ${table} DROP CONSTRAINT ${pkName};`) - } - - // - // 5. Add composite primary key - // - await queryInterface.sequelize.query( - ` - ALTER TABLE ${table} - ADD PRIMARY KEY (organization_id, organization_code, tenant_code); - ` - ) - }, - - async down(queryInterface, Sequelize) { - const table = 'organization_extension' - - // Drop composite PK - const pkNameQuery = await queryInterface.sequelize.query( - ` - SELECT constraint_name - FROM information_schema.table_constraints - WHERE table_name='${table}' AND constraint_type='PRIMARY KEY'; - `, - { type: Sequelize.QueryTypes.SELECT } - ) - - if (pkNameQuery.length > 0) { - const pkName = pkNameQuery[0].constraint_name - await queryInterface.sequelize.query(`ALTER TABLE ${table} DROP CONSTRAINT ${pkName};`) - } - - // Restore old single-column PK (optional — depends on original schema) - await queryInterface.sequelize.query( - ` - ALTER TABLE ${table} - ADD PRIMARY KEY (organization_id); - ` - ) - }, -} diff --git a/src/database/migrations/20251208154559-create-organization-extension-data.js b/src/database/migrations/20251208154559-create-organization-extension-data.js deleted file mode 100644 index cded4809b..000000000 --- a/src/database/migrations/20251208154559-create-organization-extension-data.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict' - -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.bulkInsert('organization_extension', [ - { - organization_id: process.env.DEFAULT_ORG_ID, - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, - - session_visibility_policy: 'CURRENT', - mentor_visibility_policy: 'CURRENT', - external_session_visibility_policy: 'CURRENT', - external_mentor_visibility_policy: 'CURRENT', - - approval_required_for: Sequelize.literal(`ARRAY[]::text[]`), // corrected from CSV `{}` → `[]` - allow_mentor_override: false, - - created_by: '1', - updated_by: '1', - - mentee_feedback_question_set: 'MENTEE_QS1', - mentor_feedback_question_set: 'MENTOR_QS2', - - uploads: null, - mentee_visibility_policy: 'CURRENT', - external_mentee_visibility_policy: 'CURRENT', - name: null, - theme: null, - deleted_at: null, - created_at: new Date('2025-12-08T15:34:21.220Z'), - updated_at: new Date('2025-12-08T15:34:21.220Z'), - }, - ]) - }, - - async down(queryInterface, Sequelize) { - await queryInterface.bulkDelete('organization_extension', { - organization_id: '1', - organization_code: 'default_code', - tenant_code: 'default', - }) - }, -} diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index e61fd703c..5ed8fcadc 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,8 +120,6 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -155,7 +153,6 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 - eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE entitiesFinalArray.push(eachEntity) }) diff --git a/src/database/seeders/20231103090632-seed-forms.js b/src/database/seeders/20231103090632-seed-forms.js index ffd7441f8..226453e9a 100644 --- a/src/database/seeders/20231103090632-seed-forms.js +++ b/src/database/seeders/20231103090632-seed-forms.js @@ -920,12 +920,6 @@ module.exports = { created_at: new Date(), }, ] - - formData.forEach((formInstance) => { - formInstance.tenant_code = process.env.DEFAULT_TENANT_CODE - formInstance.organization_code = process.env.DEFAULT_ORGANIZATION_CODE - }) - await queryInterface.bulkInsert('forms', formData, {}) } catch (error) { console.error('Error seeding forms:', error) From 6a12e908fc3b3ad52d5895ff065c6d695bfced61 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 23 Dec 2025 12:42:13 +0530 Subject: [PATCH 15/66] migrations updated --- ...session-columns-and-update-mentor-names.js | 2 +- src/database/queries/sessions.js | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index d451d8f41..738e2e8a6 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,7 +33,7 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const sessionsWithNullMentorName = await sessionQueries.findAll({ mentor_name: 'Mentor' }) + const sessionsWithNullMentorName = await sessionQueries.findAllMigrations({ mentor_name: 'Mentor' }) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index 3d6c79391..50d8a0e9c 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -163,6 +163,26 @@ exports.updateRecords = async (data, options = {}) => { } } +exports.findAllMigrations = async (filter, options = {}) => { + try { + // filter.tenant_code = tenantCode + + // Safe merge: tenant filtering cannot be overridden by options.where + const { where: optionsWhere, ...otherOptions } = options + + return await Session.findAll({ + where: { + ...optionsWhere, // Allow additional where conditions + ...filter, // But tenant filtering takes priority + }, + ...otherOptions, + raw: true, + }) + } catch (error) { + return error + } +} + exports.findAll = async (filter, tenantCode, options = {}) => { try { filter.tenant_code = tenantCode From c6e8050127008f74062fb8febd960391123ed037 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 23 Dec 2025 15:42:50 +0530 Subject: [PATCH 16/66] migrations fix check --- ...20240716111210-update-user-id-to-string.js | 8 ++--- .../20251020081719-add-orgEntity-type.js | 4 +-- src/database/queries/sessions.js | 31 ++++++++++++------- ...822124704-add_entity_types_and_entities.js | 4 ++- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8aaa64c97..8baafbae6 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - await queryInterface.changeColumn('session_ownerships', 'user_id', { - type: Sequelize.STRING, - allowNull: false, - }) + // await queryInterface.changeColumn('session_ownerships', 'user_id', { + // type: Sequelize.STRING, + // allowNull: false, + // }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251020081719-add-orgEntity-type.js b/src/database/migrations/20251020081719-add-orgEntity-type.js index c095015b1..5a9802805 100644 --- a/src/database/migrations/20251020081719-add-orgEntity-type.js +++ b/src/database/migrations/20251020081719-add-orgEntity-type.js @@ -28,8 +28,8 @@ module.exports = { updated_by: 0, allow_filtering: false, organization_id: defaultOrgId, - organization_code: defaultOrgCode, - tenant_code: defaultTenantCode, + //organization_code: defaultOrgCode, + // tenant_code: defaultTenantCode, has_entities: false, meta: JSON.stringify({ label: convertToWords(key), diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index 50d8a0e9c..783e29f46 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -163,21 +163,30 @@ exports.updateRecords = async (data, options = {}) => { } } -exports.findAllMigrations = async (filter, options = {}) => { +exports.findAllMigrations = async (filter) => { try { - // filter.tenant_code = tenantCode + const whereClauses = [] + const replacements = {} - // Safe merge: tenant filtering cannot be overridden by options.where - const { where: optionsWhere, ...otherOptions } = options + Object.entries(filter).forEach(([key, value]) => { + if (value === undefined) return - return await Session.findAll({ - where: { - ...optionsWhere, // Allow additional where conditions - ...filter, // But tenant filtering takes priority - }, - ...otherOptions, - raw: true, + whereClauses.push(`"${key}" = :${key}`) + replacements[key] = value + }) + + const sql = ` + SELECT * + FROM sessions + ${whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : ''} + ` + + const result = await Sequelize.query(sql, { + replacements, + type: QueryTypes.SELECT, }) + + return result } catch (error) { return error } diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index 5ed8fcadc..25f6f7e53 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,6 +120,8 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -153,7 +155,7 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 - + eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE entitiesFinalArray.push(eachEntity) }) } From fa6e05d2455fb6e663c877180958322e3f527969 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 23 Dec 2025 16:54:57 +0530 Subject: [PATCH 17/66] restored files --- .../migrations/20240716111210-update-user-id-to-string.js | 8 ++++---- .../migrations/20251020081719-add-orgEntity-type.js | 4 ++-- .../20230822124704-add_entity_types_and_entities.js | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8baafbae6..8aaa64c97 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - // await queryInterface.changeColumn('session_ownerships', 'user_id', { - // type: Sequelize.STRING, - // allowNull: false, - // }) + await queryInterface.changeColumn('session_ownerships', 'user_id', { + type: Sequelize.STRING, + allowNull: false, + }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251020081719-add-orgEntity-type.js b/src/database/migrations/20251020081719-add-orgEntity-type.js index 5a9802805..c095015b1 100644 --- a/src/database/migrations/20251020081719-add-orgEntity-type.js +++ b/src/database/migrations/20251020081719-add-orgEntity-type.js @@ -28,8 +28,8 @@ module.exports = { updated_by: 0, allow_filtering: false, organization_id: defaultOrgId, - //organization_code: defaultOrgCode, - // tenant_code: defaultTenantCode, + organization_code: defaultOrgCode, + tenant_code: defaultTenantCode, has_entities: false, meta: JSON.stringify({ label: convertToWords(key), diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index 25f6f7e53..5ed8fcadc 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,8 +120,6 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -155,7 +153,7 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 - eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE + entitiesFinalArray.push(eachEntity) }) } From 91591f95f631b1c38979bf7ead8cd8e9bceefd8b Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 17:48:40 +0530 Subject: [PATCH 18/66] feat:samigrations updates --- ...session-columns-and-update-mentor-names.js | 2 +- ...20240716111210-update-user-id-to-string.js | 8 ++--- .../20251020081719-add-orgEntity-type.js | 4 +-- src/database/queries/sessions.js | 29 +++++++++++++++++++ ...822124704-add_entity_types_and_entities.js | 3 ++ 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index d451d8f41..738e2e8a6 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,7 +33,7 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const sessionsWithNullMentorName = await sessionQueries.findAll({ mentor_name: 'Mentor' }) + const sessionsWithNullMentorName = await sessionQueries.findAllMigrations({ mentor_name: 'Mentor' }) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8aaa64c97..8baafbae6 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - await queryInterface.changeColumn('session_ownerships', 'user_id', { - type: Sequelize.STRING, - allowNull: false, - }) + // await queryInterface.changeColumn('session_ownerships', 'user_id', { + // type: Sequelize.STRING, + // allowNull: false, + // }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251020081719-add-orgEntity-type.js b/src/database/migrations/20251020081719-add-orgEntity-type.js index c095015b1..5855c95ca 100644 --- a/src/database/migrations/20251020081719-add-orgEntity-type.js +++ b/src/database/migrations/20251020081719-add-orgEntity-type.js @@ -28,8 +28,8 @@ module.exports = { updated_by: 0, allow_filtering: false, organization_id: defaultOrgId, - organization_code: defaultOrgCode, - tenant_code: defaultTenantCode, + //organization_code: defaultOrgCode, + //tenant_code: defaultTenantCode, has_entities: false, meta: JSON.stringify({ label: convertToWords(key), diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index c1a8116b4..2c08ba51f 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -171,6 +171,35 @@ exports.findAll = async (filter, tenantCode, options = {}) => { } } +exports.findAllMigrations = async (filter) => { + try { + const whereClauses = [] + const replacements = {} + + Object.entries(filter).forEach(([key, value]) => { + if (value === undefined) return + + whereClauses.push(`"${key}" = :${key}`) + replacements[key] = value + }) + + const sql = ` + SELECT * + FROM sessions + ${whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : ''} + ` + + const result = await Sequelize.query(sql, { + replacements, + type: QueryTypes.SELECT, + }) + + return result + } catch (error) { + return error + } +} + exports.updateEnrollmentCount = async (sessionId, increment = true, tenantCode) => { try { const options = increment ? { by: 1 } : { by: -1 } diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index 5ed8fcadc..e61fd703c 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,6 +120,8 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -153,6 +155,7 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 + eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE entitiesFinalArray.push(eachEntity) }) From 0914311598cd0f84cd437880fb8d1370aa87ded9 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 31 Dec 2025 13:23:43 +0530 Subject: [PATCH 19/66] savepoint-post-pr-review-1 --- ...1228184838-add-session-columns-and-update-mentor-names.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index 738e2e8a6..5ea838acd 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,7 +33,10 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const sessionsWithNullMentorName = await sessionQueries.findAllMigrations({ mentor_name: 'Mentor' }) + const sessionsWithNullMentorName = await queryInterface.sequelize.query( + "SELECT * FROM sessions WHERE mentor_name = 'Mentor'", + { type: Sequelize.QueryTypes.SELECT } + ) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') From 0a6e2286064c78f7bc2ce267278db309a3866c50 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 31 Dec 2025 15:41:41 +0530 Subject: [PATCH 20/66] addressed PR comments --- src/database/queries/sessions.js | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index 2c08ba51f..c1a8116b4 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -171,35 +171,6 @@ exports.findAll = async (filter, tenantCode, options = {}) => { } } -exports.findAllMigrations = async (filter) => { - try { - const whereClauses = [] - const replacements = {} - - Object.entries(filter).forEach(([key, value]) => { - if (value === undefined) return - - whereClauses.push(`"${key}" = :${key}`) - replacements[key] = value - }) - - const sql = ` - SELECT * - FROM sessions - ${whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : ''} - ` - - const result = await Sequelize.query(sql, { - replacements, - type: QueryTypes.SELECT, - }) - - return result - } catch (error) { - return error - } -} - exports.updateEnrollmentCount = async (sessionId, increment = true, tenantCode) => { try { const options = increment ? { by: 1 } : { by: -1 } From db6f603fa0bc436bb186a7b2f61d976fe7461024 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 5 Jan 2026 18:02:08 +0530 Subject: [PATCH 21/66] new fix --- src/start.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/start.sh b/src/start.sh index cc4019deb..b358e26e0 100755 --- a/src/start.sh +++ b/src/start.sh @@ -4,11 +4,11 @@ set -e # Replace kafka.js with kafka.ci.js cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js -echo "[MENTORING] Running migrations..." -npm run db:init:integration +# echo "[MENTORING] Running migrations..." +# npm run db:init:integration -echo "[MENTORING] Running seeds..." -npm run db:seed:all:integration +# echo "[MENTORING] Running seeds..." +# npm run db:seed:all:integration echo "[MENTORING] Starting application..." npm run dev From c2d044499b1cdc1bb16de71a5add1145dc4390f2 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 5 Jan 2026 18:09:34 +0530 Subject: [PATCH 22/66] new fix --- .circleci/config.yml | 208 ++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 113 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index aecd88282..4069ad120 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,170 +5,152 @@ jobs: machine: image: ubuntu-2004:2024.05.1 docker_layer_caching: true + working_directory: ~/mentoring steps: - - run: - name: Configure git to use HTTPS - command: git config --global url."https://github.com/".insteadOf "git@github.com:" - - - checkout: - path: ~/mentoring/ + # ------------------------ + # Checkout + # ------------------------ + - checkout - # Restore mentoring dependencies + # ------------------------ + # Install Mentoring deps + # ------------------------ - restore_cache: - key: mentoring-dependency-cache-{{ checksum "src/package.json" }} + key: mentoring-deps-{{ checksum "src/package.json" }} - run: - name: Install dependencies - command: cd src/ && npm install + name: Install Mentoring dependencies + command: | + cd src + npm install - save_cache: - key: mentoring-dependency-cache-{{ checksum "src/package.json" }} + key: mentoring-deps-{{ checksum "src/package.json" }} paths: - - ./src/node_modules - - # Disable SSH rewrite - - run: - name: Disable SSH rewrite - command: git config --global --unset url."ssh://git@github.com".insteadOf || true + - src/node_modules - # Clone USER service + # ------------------------ + # Clone dependent services + # ------------------------ - run: name: Clone User service - command: cd ../ && git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch + command: | + cd .. + git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch - restore_cache: - key: user-dependency-cache-{{ checksum "../user/src/package.json" }} + key: user-deps-{{ checksum "../user/src/package.json" }} - run: - name: Install User service dependencies - command: cd ../user/src/ && npm install + name: Install User dependencies + command: | + cd ../user/src + npm install - save_cache: - key: user-dependency-cache-{{ checksum "../user/src/package.json" }} + key: user-deps-{{ checksum "../user/src/package.json" }} paths: - ../user/src/node_modules - # Clone Scheduler service (optional) - - run: - name: Clone Scheduler service - command: cd ../ && git clone https://github.com/ELEVATE-Project/scheduler.git --branch dev --single-branch || true - - # Clone Communications service + # ------------------------ + # Config + # ------------------------ - run: - name: Clone Communications service - command: cd ../ && git clone https://github.com/ELEVATE-Project/chat-communications.git --branch dev --single-branch || true - + name: Copy integration config + command: | + cp dev-ops/sample_config.json src/config.json + # ------------------------ + # Start infrastructure only + # ------------------------ - run: - name: Copy config.json into mentoring src - command: cp dev-ops/sample_config.json src/config.json - # ------------------------------- - # FIXED: Start docker-compose (DETACHED) - # ------------------------------- - - run: - name: Start docker containers + name: Start infrastructure containers command: | - cd dev-ops/ - docker-compose pull || true - docker-compose build || true - docker-compose up -d - sleep 20 - - # ------------------------------- - # FIXED: Wait for services to become healthy - # ------------------------------- + cd dev-ops + docker-compose pull + docker-compose build + docker-compose up -d citus kafka redis + + # ------------------------ + # Wait for DB readiness + # ------------------------ - run: - name: Wait for containers to become healthy + name: Wait for Postgres to accept connections command: | - cd dev-ops/ - echo "Waiting for services to pass healthchecks..." - for i in {1..30}; do - unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}") - starting=$(docker ps --filter "health=starting" --format "{{.Names}}") - - if [ -z "$unhealthy" ] && [ -z "$starting" ]; then - echo "All services healthy." + if docker exec citus pg_isready -U postgres; then + echo "Database is ready" break fi - - echo "Still waiting... (attempt $i)" - docker ps --format "table {{.Names}}\t{{.Status}}" - sleep 5 + echo "Waiting for database..." + sleep 2 done - echo "Final container status:" - docker ps --format "table {{.Names}}\t{{.Status}}" - - # ------------------------------- - # Diagnostics if services fail - # ------------------------------- + # ------------------------ + # Run migrations (CLI) + # ------------------------ - run: - name: Print logs on failure - when: always + name: Run DB migrations command: | - cd dev-ops/ - echo "Kafka logs:" - docker-compose logs --tail=200 kafka || true - echo "Citus logs:" - docker-compose logs --tail=200 citus || true - echo "Mentoring logs:" - docker-compose logs --tail=200 mentoring || true - echo "User logs:" - docker-compose logs --tail=200 user || true - + cd src + npx sequelize-cli db:migrate --env integration + # ------------------------ + # Run seeders (CLI) + # ------------------------ - run: - name: Wait for mentoring MIGRATION_SUCCESS + name: Run DB seeders command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for MIGRATION_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then - echo "Migrations completed." - break - fi - echo "Waiting for migrations... ($i)" - sleep 3 - done - + cd src + npx sequelize-cli db:seed:all --env integration - - # ------------------------ - # Wait for seeders + # ------------------------ + # Start application services # ------------------------ - run: - name: Wait for mentoring SEEDER_SUCCESS + name: Start application containers command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for SEEDER_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then - echo "Seeders completed." - break - fi - echo "Waiting for seeders... ($i)" - sleep 3 - done + cd dev-ops + docker-compose up -d mentoring user + + # ------------------------ + # Optional: wait for app ports + # ------------------------ + # - run: + # name: Wait for mentoring service + # command: | + # for i in {1..30}; do + # if curl -sf http://localhost:3000/health; then + # echo "Mentoring service is up" + # break + # fi + # sleep 2 + # done - # ------------------------------- - # Run Integration Tests - # ------------------------------- + # ------------------------ + # Run integration tests + # ------------------------ - run: - name: Running integration tests + name: Run integration tests command: | - cd src/ + cd src npm run test:integration + # ------------------------ + # Store results + # ------------------------ - store_test_results: - path: ./dev-ops/report + path: dev-ops/report workflows: build-and-test: jobs: - build: filters: - tags: - only: \b(dev|develop|main)\b + branches: + only: + - dev + - develop + - main \ No newline at end of file From cbaf6582e0770639a34512ff42d80a907af897ea Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 5 Jan 2026 18:18:09 +0530 Subject: [PATCH 23/66] new fix-1 --- src/start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/start.sh b/src/start.sh index b358e26e0..ffecd14d9 100755 --- a/src/start.sh +++ b/src/start.sh @@ -9,6 +9,7 @@ cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js # echo "[MENTORING] Running seeds..." # npm run db:seed:all:integration +# running script echo "[MENTORING] Starting application..." npm run dev From 9d0b5ff00726243c7c2095c1562de43c4006c331 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 5 Jan 2026 23:47:34 +0530 Subject: [PATCH 24/66] fix-2 --- src/start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/start.sh b/src/start.sh index ffecd14d9..7f9a91ded 100755 --- a/src/start.sh +++ b/src/start.sh @@ -10,6 +10,7 @@ cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js # echo "[MENTORING] Running seeds..." # npm run db:seed:all:integration # running script +# running script 2 echo "[MENTORING] Starting application..." npm run dev From 82f2a1b648361348f48752625dad041b4a69c13c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:16:25 +0530 Subject: [PATCH 25/66] fix-3 --- src/start.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/start.sh b/src/start.sh index 7f9a91ded..cc4019deb 100755 --- a/src/start.sh +++ b/src/start.sh @@ -4,13 +4,11 @@ set -e # Replace kafka.js with kafka.ci.js cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js -# echo "[MENTORING] Running migrations..." -# npm run db:init:integration +echo "[MENTORING] Running migrations..." +npm run db:init:integration -# echo "[MENTORING] Running seeds..." -# npm run db:seed:all:integration -# running script -# running script 2 +echo "[MENTORING] Running seeds..." +npm run db:seed:all:integration echo "[MENTORING] Starting application..." npm run dev From df2bcd62d495a603b9be11c0e32e79fe3a25ad25 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:16:38 +0530 Subject: [PATCH 26/66] fix-3.1 --- .circleci/config.yml | 208 +++++++++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 95 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4069ad120..aecd88282 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,152 +5,170 @@ jobs: machine: image: ubuntu-2004:2024.05.1 docker_layer_caching: true - working_directory: ~/mentoring steps: - # ------------------------ - # Checkout - # ------------------------ - - checkout + - run: + name: Configure git to use HTTPS + command: git config --global url."https://github.com/".insteadOf "git@github.com:" - # ------------------------ - # Install Mentoring deps - # ------------------------ + - checkout: + path: ~/mentoring/ + + # Restore mentoring dependencies - restore_cache: - key: mentoring-deps-{{ checksum "src/package.json" }} + key: mentoring-dependency-cache-{{ checksum "src/package.json" }} - run: - name: Install Mentoring dependencies - command: | - cd src - npm install + name: Install dependencies + command: cd src/ && npm install - save_cache: - key: mentoring-deps-{{ checksum "src/package.json" }} + key: mentoring-dependency-cache-{{ checksum "src/package.json" }} paths: - - src/node_modules + - ./src/node_modules - # ------------------------ - # Clone dependent services - # ------------------------ + # Disable SSH rewrite + - run: + name: Disable SSH rewrite + command: git config --global --unset url."ssh://git@github.com".insteadOf || true + + # Clone USER service - run: name: Clone User service - command: | - cd .. - git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch + command: cd ../ && git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch - restore_cache: - key: user-deps-{{ checksum "../user/src/package.json" }} + key: user-dependency-cache-{{ checksum "../user/src/package.json" }} - run: - name: Install User dependencies - command: | - cd ../user/src - npm install + name: Install User service dependencies + command: cd ../user/src/ && npm install - save_cache: - key: user-deps-{{ checksum "../user/src/package.json" }} + key: user-dependency-cache-{{ checksum "../user/src/package.json" }} paths: - ../user/src/node_modules - # ------------------------ - # Config - # ------------------------ + # Clone Scheduler service (optional) - run: - name: Copy integration config - command: | - cp dev-ops/sample_config.json src/config.json + name: Clone Scheduler service + command: cd ../ && git clone https://github.com/ELEVATE-Project/scheduler.git --branch dev --single-branch || true - # ------------------------ - # Start infrastructure only - # ------------------------ + # Clone Communications service - run: - name: Start infrastructure containers - command: | - cd dev-ops - docker-compose pull - docker-compose build - docker-compose up -d citus kafka redis + name: Clone Communications service + command: cd ../ && git clone https://github.com/ELEVATE-Project/chat-communications.git --branch dev --single-branch || true - # ------------------------ - # Wait for DB readiness - # ------------------------ + + - run: + name: Copy config.json into mentoring src + command: cp dev-ops/sample_config.json src/config.json + # ------------------------------- + # FIXED: Start docker-compose (DETACHED) + # ------------------------------- + - run: + name: Start docker containers + command: | + cd dev-ops/ + docker-compose pull || true + docker-compose build || true + docker-compose up -d + sleep 20 + + # ------------------------------- + # FIXED: Wait for services to become healthy + # ------------------------------- - run: - name: Wait for Postgres to accept connections + name: Wait for containers to become healthy command: | + cd dev-ops/ + echo "Waiting for services to pass healthchecks..." + for i in {1..30}; do - if docker exec citus pg_isready -U postgres; then - echo "Database is ready" + unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}") + starting=$(docker ps --filter "health=starting" --format "{{.Names}}") + + if [ -z "$unhealthy" ] && [ -z "$starting" ]; then + echo "All services healthy." break fi - echo "Waiting for database..." - sleep 2 + + echo "Still waiting... (attempt $i)" + docker ps --format "table {{.Names}}\t{{.Status}}" + sleep 5 done - # ------------------------ - # Run migrations (CLI) - # ------------------------ + echo "Final container status:" + docker ps --format "table {{.Names}}\t{{.Status}}" + + # ------------------------------- + # Diagnostics if services fail + # ------------------------------- - run: - name: Run DB migrations + name: Print logs on failure + when: always command: | - cd src - npx sequelize-cli db:migrate --env integration + cd dev-ops/ + echo "Kafka logs:" + docker-compose logs --tail=200 kafka || true + echo "Citus logs:" + docker-compose logs --tail=200 citus || true + echo "Mentoring logs:" + docker-compose logs --tail=200 mentoring || true + echo "User logs:" + docker-compose logs --tail=200 user || true + - # ------------------------ - # Run seeders (CLI) - # ------------------------ - run: - name: Run DB seeders + name: Wait for mentoring MIGRATION_SUCCESS command: | - cd src - npx sequelize-cli db:seed:all --env integration + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for MIGRATION_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then + echo "Migrations completed." + break + fi + echo "Waiting for migrations... ($i)" + sleep 3 + done - # ------------------------ - # Start application services + + + # ------------------------ + # Wait for seeders # ------------------------ - run: - name: Start application containers + name: Wait for mentoring SEEDER_SUCCESS command: | - cd dev-ops - docker-compose up -d mentoring user - - # ------------------------ - # Optional: wait for app ports - # ------------------------ - # - run: - # name: Wait for mentoring service - # command: | - # for i in {1..30}; do - # if curl -sf http://localhost:3000/health; then - # echo "Mentoring service is up" - # break - # fi - # sleep 2 - # done + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for SEEDER_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then + echo "Seeders completed." + break + fi + echo "Waiting for seeders... ($i)" + sleep 3 + done - # ------------------------ - # Run integration tests - # ------------------------ + # ------------------------------- + # Run Integration Tests + # ------------------------------- - run: - name: Run integration tests + name: Running integration tests command: | - cd src + cd src/ npm run test:integration - # ------------------------ - # Store results - # ------------------------ - store_test_results: - path: dev-ops/report + path: ./dev-ops/report workflows: build-and-test: jobs: - build: filters: - branches: - only: - - dev - - develop - - main \ No newline at end of file + tags: + only: \b(dev|develop|main)\b From a364f7746335219d17055850f38a27ec3e96b597 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:24:09 +0530 Subject: [PATCH 27/66] fix-4 --- .circleci/config.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index aecd88282..bd77a8a08 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -73,8 +73,7 @@ jobs: cd dev-ops/ docker-compose pull || true docker-compose build || true - docker-compose up -d - sleep 20 + docker-compose up -d citus kafka redis # ------------------------------- # FIXED: Wait for services to become healthy @@ -119,7 +118,27 @@ jobs: echo "User logs:" docker-compose logs --tail=200 user || true + - run: + name: Run DB migrations + command: | + cd src + npx sequelize-cli db:migrate + + - run: + name: Run DB seeders + command: | + cd src + npx sequelize-cli db:seed:all + # ------------------------ + # Start application services + # ------------------------ + - run: + name: Start application containers + command: | + cd dev-ops + docker-compose up -d mentoring user + - run: name: Wait for mentoring MIGRATION_SUCCESS command: | From f94947d50340b13d72e3d787b2f65f3c39e53ce2 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:39:41 +0530 Subject: [PATCH 28/66] fix-5 --- .circleci/config.yml | 33 --------------------------------- dev-ops/docker-compose.yml | 2 +- 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd77a8a08..447a39b4e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -139,39 +139,6 @@ jobs: cd dev-ops docker-compose up -d mentoring user - - run: - name: Wait for mentoring MIGRATION_SUCCESS - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for MIGRATION_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then - echo "Migrations completed." - break - fi - echo "Waiting for migrations... ($i)" - sleep 3 - done - - - - # ------------------------ - # Wait for seeders - # ------------------------ - - run: - name: Wait for mentoring SEEDER_SUCCESS - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for SEEDER_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then - echo "Seeders completed." - break - fi - echo "Waiting for seeders... ($i)" - sleep 3 - done - # ------------------------------- # Run Integration Tests # ------------------------------- diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 9e1dd9ce3..235fa7009 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -77,7 +77,7 @@ services: ports: - '3000:3000' - '9229:9229' - command: ["sh", "/var/src/start.sh"] + command: npm run dev environment: - KAFKA_URL=kafka:9092 From e9a15589b0c014afb990d6dd19c92c324f089bd9 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:46:37 +0530 Subject: [PATCH 29/66] fix-6 --- dev-ops/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 235fa7009..dd5b6d72c 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -97,7 +97,7 @@ services: networks: - elevate_net env_file: - - ./integration_test.mentoring.env + - integration_test.mentoring.env user: build: '../../user/' @@ -129,7 +129,7 @@ services: networks: - elevate_net env_file: - - ./integration_test.user.env + - integration_test.user.env networks: elevate_net: From aea283d7a89fef8f1b7524fcf3ff5af60f62b30e Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 6 Jan 2026 00:53:40 +0530 Subject: [PATCH 30/66] fix-7 --- .circleci/config.yml | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 447a39b4e..aecd88282 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -73,7 +73,8 @@ jobs: cd dev-ops/ docker-compose pull || true docker-compose build || true - docker-compose up -d citus kafka redis + docker-compose up -d + sleep 20 # ------------------------------- # FIXED: Wait for services to become healthy @@ -118,27 +119,40 @@ jobs: echo "User logs:" docker-compose logs --tail=200 user || true - - run: - name: Run DB migrations - command: | - cd src - npx sequelize-cli db:migrate - run: - name: Run DB seeders + name: Wait for mentoring MIGRATION_SUCCESS command: | - cd src - npx sequelize-cli db:seed:all + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for MIGRATION_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then + echo "Migrations completed." + break + fi + echo "Waiting for migrations... ($i)" + sleep 3 + done - # ------------------------ - # Start application services + + + # ------------------------ + # Wait for seeders # ------------------------ - run: - name: Start application containers + name: Wait for mentoring SEEDER_SUCCESS command: | - cd dev-ops - docker-compose up -d mentoring user - + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Waiting for SEEDER_SUCCESS..." + for i in {1..60}; do + if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then + echo "Seeders completed." + break + fi + echo "Waiting for seeders... ($i)" + sleep 3 + done + # ------------------------------- # Run Integration Tests # ------------------------------- From dd992f33baaaae25226dc8c57bea687170021111 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 12 Jan 2026 17:33:23 +0530 Subject: [PATCH 31/66] updated migration scripts and seeders --- .../20230609052656-create-user-extension.js | 8 +++ ...612030911-create-organisation-extension.js | 8 +++ ...ltering-session-ownership-tabe-and-data.js | 57 +++++++++++++++++++ ...4041327-add_questions_and_question_sets.js | 4 ++ .../seeders/20231103090632-seed-forms.js | 6 ++ ...20231115170949-add-new-mentee-questions.js | 2 + 6 files changed, 85 insertions(+) create mode 100644 src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js diff --git a/src/database/migrations/20230609052656-create-user-extension.js b/src/database/migrations/20230609052656-create-user-extension.js index 6b17c1378..e12f48fb1 100644 --- a/src/database/migrations/20230609052656-create-user-extension.js +++ b/src/database/migrations/20230609052656-create-user-extension.js @@ -47,6 +47,14 @@ module.exports = { custom_entity_text: { type: Sequelize.JSON, }, + organization_code: { + type: Sequelize.STRING, + allowNull: false, + }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20230612030911-create-organisation-extension.js b/src/database/migrations/20230612030911-create-organisation-extension.js index e4f6fd6ed..61c45fc07 100644 --- a/src/database/migrations/20230612030911-create-organisation-extension.js +++ b/src/database/migrations/20230612030911-create-organisation-extension.js @@ -27,6 +27,14 @@ module.exports = { allow_mentor_override: { type: Sequelize.BOOLEAN, }, + organization_code: { + type: Sequelize.STRING, + allowNull: false, + }, + tenant_code: { + type: Sequelize.STRING, + allowNull: false, + }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js b/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js new file mode 100644 index 000000000..530fe5b78 --- /dev/null +++ b/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js @@ -0,0 +1,57 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + // Remove the primary key constraint on mentor_id + await queryInterface.removeConstraint('session_ownerships', 'session_ownerships_pkey') + // change mentor_id column name + await queryInterface.renameColumn('session_ownerships', 'mentor_id', 'user_id') + // add new column called type + await queryInterface.addColumn('session_ownerships', 'type', { + allowNull: false, + type: Sequelize.STRING, + defaultValue: 'MENTOR', + primaryKey: true, + }) + + // create new entries for creator type for old data + const oldSessionOwnerships = await queryInterface.sequelize.query('SELECT * FROM session_ownerships', { + type: Sequelize.QueryTypes.SELECT, + }) + // Do only if old data is present + if (oldSessionOwnerships.length > 0) { + const creatorEntries = oldSessionOwnerships.map((entry) => ({ + ...entry, + type: 'CREATOR', + })) + + await queryInterface.bulkInsert('session_ownerships', creatorEntries) + } + + await queryInterface.addConstraint('session_ownerships', { + fields: ['user_id', 'session_id', 'type'], + type: 'primary key', + name: 'session_ownerships_user_session_type_pkey', + }) + }, + + async down(queryInterface, Sequelize) { + // Delete all entries where type='CREATOR' + await queryInterface.bulkDelete('session_ownerships', { type: 'CREATOR' }) + + // remove added primarykey constrain + await queryInterface.removeConstraint('session_ownerships', 'session_ownerships_user_session_type_pkey') + // revert column change + await queryInterface.renameColumn('session_ownerships', 'user_id', 'mentor_id') + // remove type column + await queryInterface.removeColumn('session_ownerships', 'type') + + // Add back the primary key constraint on mentor_id + await queryInterface.addConstraint('session_ownerships', { + fields: ['mentor_id', 'session_id'], + type: 'primary key', + name: 'session_ownerships_pkey', + }) + }, +} diff --git a/src/database/seeders/20230824041327-add_questions_and_question_sets.js b/src/database/seeders/20230824041327-add_questions_and_question_sets.js index cd6aea0e5..ac1d9c45a 100644 --- a/src/database/seeders/20230824041327-add_questions_and_question_sets.js +++ b/src/database/seeders/20230824041327-add_questions_and_question_sets.js @@ -104,6 +104,8 @@ module.exports = { status: 'PUBLISHED', updated_at: new Date(), created_at: new Date(), + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, } questionSetFinalArray.push(questionSetRow) @@ -116,6 +118,8 @@ module.exports = { questionsArray[questionSet].forEach((question) => { question.created_at = new Date() question.updated_at = new Date() + question.organization_code = process.env.DEFAULT_ORGANIZATION_CODE + question.tenant_code = process.env.DEFAULT_TENANT_CODE question.rendering_data = JSON.stringify(question.rendering_data) if (question.category) { diff --git a/src/database/seeders/20231103090632-seed-forms.js b/src/database/seeders/20231103090632-seed-forms.js index 226453e9a..266b38699 100644 --- a/src/database/seeders/20231103090632-seed-forms.js +++ b/src/database/seeders/20231103090632-seed-forms.js @@ -920,6 +920,12 @@ module.exports = { created_at: new Date(), }, ] + + formData.forEach((form) => { + form.organization_code = process.env.DEFAULT_ORGANIZATION_CODE + form.tenant_code = process.env.DEFAULT_TENANT_CODE + }) + await queryInterface.bulkInsert('forms', formData, {}) } catch (error) { console.error('Error seeding forms:', error) diff --git a/src/database/seeders/20231115170949-add-new-mentee-questions.js b/src/database/seeders/20231115170949-add-new-mentee-questions.js index 24186a23b..09cb2568c 100644 --- a/src/database/seeders/20231115170949-add-new-mentee-questions.js +++ b/src/database/seeders/20231115170949-add-new-mentee-questions.js @@ -54,6 +54,8 @@ module.exports = { }, updated_at: new Date(), created_at: new Date(), + organization_code: process.env.DEFAULT_ORGANIZATION_CODE, + tenant_code: process.env.DEFAULT_TENANT_CODE, } questionsFinalArray = questionsArray.map((question) => ({ ...question, ...additionalObject })) From 9834782a6e655997d0d06148517af71fba989677 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 12 Jan 2026 18:18:57 +0530 Subject: [PATCH 32/66] savepoint-1 --- dev-ops/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index dd5b6d72c..9e1dd9ce3 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -77,7 +77,7 @@ services: ports: - '3000:3000' - '9229:9229' - command: npm run dev + command: ["sh", "/var/src/start.sh"] environment: - KAFKA_URL=kafka:9092 @@ -97,7 +97,7 @@ services: networks: - elevate_net env_file: - - integration_test.mentoring.env + - ./integration_test.mentoring.env user: build: '../../user/' @@ -129,7 +129,7 @@ services: networks: - elevate_net env_file: - - integration_test.user.env + - ./integration_test.user.env networks: elevate_net: From 7b30729d765c06e94e636832dd4b16cf00d26a8c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Mon, 12 Jan 2026 18:48:09 +0530 Subject: [PATCH 33/66] done the changes --- .../migrations/20240716111210-update-user-id-to-string.js | 8 ++++---- .../migrations/20251020081719-add-orgEntity-type.js | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8baafbae6..8aaa64c97 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - // await queryInterface.changeColumn('session_ownerships', 'user_id', { - // type: Sequelize.STRING, - // allowNull: false, - // }) + await queryInterface.changeColumn('session_ownerships', 'user_id', { + type: Sequelize.STRING, + allowNull: false, + }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251020081719-add-orgEntity-type.js b/src/database/migrations/20251020081719-add-orgEntity-type.js index 5855c95ca..1bcaea75f 100644 --- a/src/database/migrations/20251020081719-add-orgEntity-type.js +++ b/src/database/migrations/20251020081719-add-orgEntity-type.js @@ -28,8 +28,6 @@ module.exports = { updated_by: 0, allow_filtering: false, organization_id: defaultOrgId, - //organization_code: defaultOrgCode, - //tenant_code: defaultTenantCode, has_entities: false, meta: JSON.stringify({ label: convertToWords(key), From 4456acdcfb88e6df4d442c8c8d6499119922745f Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:08:18 +0530 Subject: [PATCH 34/66] circleci file updated --- .circleci/config.yml | 147 +++++++++++++++++++++---------------- dev-ops/docker-compose.yml | 2 +- 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index aecd88282..559211890 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,9 +5,13 @@ jobs: machine: image: ubuntu-2004:2024.05.1 docker_layer_caching: true + working_directory: ~/mentoring steps: + # ------------------------------- + # Git setup + # ------------------------------- - run: name: Configure git to use HTTPS command: git config --global url."https://github.com/".insteadOf "git@github.com:" @@ -15,153 +19,168 @@ jobs: - checkout: path: ~/mentoring/ + # ------------------------------- # Restore mentoring dependencies + # ------------------------------- - restore_cache: key: mentoring-dependency-cache-{{ checksum "src/package.json" }} - run: - name: Install dependencies - command: cd src/ && npm install + name: Install mentoring dependencies + command: | + cd src + npm install - save_cache: key: mentoring-dependency-cache-{{ checksum "src/package.json" }} paths: - ./src/node_modules + # ------------------------------- # Disable SSH rewrite + # ------------------------------- - run: name: Disable SSH rewrite command: git config --global --unset url."ssh://git@github.com".insteadOf || true + # ------------------------------- # Clone USER service + # ------------------------------- - run: name: Clone User service - command: cd ../ && git clone https://github.com/ELEVATE-Project/user.git --branch mentoring_integration_test_setup --single-branch + command: | + cd .. + git clone https://github.com/ELEVATE-Project/user.git \ + --branch mentoring_integration_test_setup \ + --single-branch - restore_cache: key: user-dependency-cache-{{ checksum "../user/src/package.json" }} - run: name: Install User service dependencies - command: cd ../user/src/ && npm install + command: | + cd ../user/src + npm install - save_cache: key: user-dependency-cache-{{ checksum "../user/src/package.json" }} paths: - ../user/src/node_modules - # Clone Scheduler service (optional) + # ------------------------------- + # Clone optional services + # ------------------------------- - run: name: Clone Scheduler service - command: cd ../ && git clone https://github.com/ELEVATE-Project/scheduler.git --branch dev --single-branch || true + command: | + cd .. + git clone https://github.com/ELEVATE-Project/scheduler.git \ + --branch dev \ + --single-branch || true - # Clone Communications service - run: name: Clone Communications service - command: cd ../ && git clone https://github.com/ELEVATE-Project/chat-communications.git --branch dev --single-branch || true - + command: | + cd .. + git clone https://github.com/ELEVATE-Project/chat-communications.git \ + --branch dev \ + --single-branch || true + # ------------------------------- + # Config + # ------------------------------- - run: - name: Copy config.json into mentoring src + name: Copy config.json command: cp dev-ops/sample_config.json src/config.json + # ------------------------------- - # FIXED: Start docker-compose (DETACHED) + # Start docker-compose # ------------------------------- - run: name: Start docker containers command: | - cd dev-ops/ + cd dev-ops docker-compose pull || true - docker-compose build || true + docker-compose build docker-compose up -d sleep 20 # ------------------------------- - # FIXED: Wait for services to become healthy + # Wait for healthchecks # ------------------------------- - run: - name: Wait for containers to become healthy + name: Wait for services to be healthy command: | - cd dev-ops/ - echo "Waiting for services to pass healthchecks..." - + cd dev-ops + echo "Waiting for containers to be healthy..." + for i in {1..30}; do unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}") starting=$(docker ps --filter "health=starting" --format "{{.Names}}") if [ -z "$unhealthy" ] && [ -z "$starting" ]; then - echo "All services healthy." + echo "All containers are healthy" break fi - echo "Still waiting... (attempt $i)" + echo "Waiting... attempt $i" docker ps --format "table {{.Names}}\t{{.Status}}" sleep 5 done - echo "Final container status:" - docker ps --format "table {{.Names}}\t{{.Status}}" - # ------------------------------- - # Diagnostics if services fail + # CI-specific Kafka config # ------------------------------- - run: - name: Print logs on failure - when: always + name: Configure Kafka for CI command: | - cd dev-ops/ - echo "Kafka logs:" - docker-compose logs --tail=200 kafka || true - echo "Citus logs:" - docker-compose logs --tail=200 citus || true - echo "Mentoring logs:" - docker-compose logs --tail=200 mentoring || true - echo "User logs:" - docker-compose logs --tail=200 user || true - + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + docker exec "$MENTORING" \ + sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' + # ------------------------------- + # Run migrations (EXPLICIT) + # ------------------------------- - run: - name: Wait for mentoring MIGRATION_SUCCESS + name: Run mentoring migrations command: | export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for MIGRATION_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "MIGRATION_SUCCESS"; then - echo "Migrations completed." - break - fi - echo "Waiting for migrations... ($i)" - sleep 3 - done - - + echo "Running migrations in $MENTORING" + docker exec "$MENTORING" npm run db:init:integration - # ------------------------ - # Wait for seeders - # ------------------------ + # ------------------------------- + # Run seeders (EXPLICIT) + # ------------------------------- - run: - name: Wait for mentoring SEEDER_SUCCESS + name: Run mentoring seeders command: | export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Waiting for SEEDER_SUCCESS..." - for i in {1..60}; do - if docker logs "$MENTORING" 2>&1 | grep -q "SEEDER_SUCCESS"; then - echo "Seeders completed." - break - fi - echo "Waiting for seeders... ($i)" - sleep 3 - done + echo "Running seeders in $MENTORING" + docker exec "$MENTORING" npm run db:seed:all:integration # ------------------------------- - # Run Integration Tests + # Run integration tests # ------------------------------- - run: - name: Running integration tests + name: Run integration tests command: | - cd src/ + cd src npm run test:integration + # ------------------------------- + # Diagnostics + # ------------------------------- + - run: + name: Print logs on failure + when: always + command: | + cd dev-ops + docker-compose logs --tail=200 kafka || true + docker-compose logs --tail=200 citus || true + docker-compose logs --tail=200 mentoring || true + docker-compose logs --tail=200 user || true + - store_test_results: path: ./dev-ops/report diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 9e1dd9ce3..1af83a62e 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -77,7 +77,7 @@ services: ports: - '3000:3000' - '9229:9229' - command: ["sh", "/var/src/start.sh"] + command: ["npm", "run", "dev"] environment: - KAFKA_URL=kafka:9092 From db052d21b4fbbf5f6a170745741125676cf42192 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:14:56 +0530 Subject: [PATCH 35/66] circleci update with correct npm commands --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 559211890..e875c16d9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -147,7 +147,7 @@ jobs: command: | export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) echo "Running migrations in $MENTORING" - docker exec "$MENTORING" npm run db:init:integration + docker exec "$MENTORING" npm run db:init # ------------------------------- # Run seeders (EXPLICIT) @@ -157,7 +157,7 @@ jobs: command: | export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) echo "Running seeders in $MENTORING" - docker exec "$MENTORING" npm run db:seed:all:integration + docker exec "$MENTORING" npm run db:seed:all # ------------------------------- # Run integration tests From 6f5178c85b512c4aea9cda8eb1ced3344bcf7adf Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:24:12 +0530 Subject: [PATCH 36/66] copying kafka file is fixed --- .circleci/config.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e875c16d9..0b055620c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,8 +91,10 @@ jobs: # Config # ------------------------------- - run: - name: Copy config.json - command: cp dev-ops/sample_config.json src/config.json + name: Prepare Kafka config for CI + command: | + cp src/configs/kafka.ci.js src/configs/kafka.js + # ------------------------------- # Start docker-compose From b646efa3fa827ff9524b302716a3cea5ee8dabcf Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:28:54 +0530 Subject: [PATCH 37/66] copying kafka file is fixed-1 --- .circleci/config.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0b055620c..ee8ce7641 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,9 +91,11 @@ jobs: # Config # ------------------------------- - run: - name: Prepare Kafka config for CI + name: Configure Kafka for CI command: | - cp src/configs/kafka.ci.js src/configs/kafka.js + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + docker exec "$MENTORING" \ + sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' # ------------------------------- @@ -134,12 +136,12 @@ jobs: # ------------------------------- # CI-specific Kafka config # ------------------------------- - - run: - name: Configure Kafka for CI - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - docker exec "$MENTORING" \ - sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' + # - run: + # name: Configure Kafka for CI + # command: | + # export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + # docker exec "$MENTORING" \ + # sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' # ------------------------------- # Run migrations (EXPLICIT) From 74a1dddb25c699fe36678d2dd5c842f8b701e177 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:33:55 +0530 Subject: [PATCH 38/66] copying kafka file is fixed-2 --- .circleci/config.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ee8ce7641..dd0ebc995 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,17 +87,6 @@ jobs: --branch dev \ --single-branch || true - # ------------------------------- - # Config - # ------------------------------- - - run: - name: Configure Kafka for CI - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - docker exec "$MENTORING" \ - sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' - - # ------------------------------- # Start docker-compose # ------------------------------- @@ -136,12 +125,12 @@ jobs: # ------------------------------- # CI-specific Kafka config # ------------------------------- - # - run: - # name: Configure Kafka for CI - # command: | - # export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - # docker exec "$MENTORING" \ - # sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' + - run: + name: Configure Kafka for CI + command: | + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + docker exec "$MENTORING" \ + sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' # ------------------------------- # Run migrations (EXPLICIT) From 764e094cb3c806ea4c9ae115e249f442edf7a535 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:38:43 +0530 Subject: [PATCH 39/66] copying kafka file is fixed-3 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dd0ebc995..2b250f888 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -183,4 +183,4 @@ workflows: - build: filters: tags: - only: \b(dev|develop|main)\b + only: \b(dev|develop|main)\b \ No newline at end of file From 3b9947f1fbf30ce6a73d8bcb5811809b90703841 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 11:43:44 +0530 Subject: [PATCH 40/66] copying kafka file is fixed-4 --- src/config.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/config.json diff --git a/src/config.json b/src/config.json new file mode 100644 index 000000000..b8468f1e0 --- /dev/null +++ b/src/config.json @@ -0,0 +1,11 @@ +{ + "authTokenUserInformation": { + "id": "data.id", + "name": "data.name", + "organization_code": "data.organizations[?id={{organization_id}}].code", + "organization_id": "data.organization_ids[0]", + "organizations": "data.organizations", + "roles": "data.organizations[?id={{organization_id}}].roles", + "tenant_code": "data.tenant_code" + } +} From baf74f4a60d92d1396822c79bb99ec3cd85c4e8c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 15:14:33 +0530 Subject: [PATCH 41/66] updated circleci file --- .circleci/config.yml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2b250f888..58e2e7c6b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,6 +87,19 @@ jobs: --branch dev \ --single-branch || true + # ------------------------------- + # Config + # ------------------------------- + - run: + name: Copy config.json + command: cp dev-ops/sample_config.json src/config.json + + - run: + name: Prepare Kafka config for CI + command: | + cp src/configs/kafka.ci.js src/configs/kafka.js + + # ------------------------------- # Start docker-compose # ------------------------------- @@ -125,12 +138,12 @@ jobs: # ------------------------------- # CI-specific Kafka config # ------------------------------- - - run: - name: Configure Kafka for CI - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - docker exec "$MENTORING" \ - sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' + # - run: + # name: Configure Kafka for CI + # command: | + # export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + # docker exec "$MENTORING" \ + # sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' # ------------------------------- # Run migrations (EXPLICIT) From 02964dcba10f629921a16d03271971a1b16b9829 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 15:38:23 +0530 Subject: [PATCH 42/66] kafka file updated. --- src/configs/kafka.js | 150 ++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 44 deletions(-) diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 3ebb08fab..f7bdbebde 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -43,74 +43,136 @@ module.exports = async () => { } async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ groupId: process.env.KAFKA_GROUP_ID }) + const consumer = kafkaClient.consumer({ + groupId: process.env.KAFKA_GROUP_ID, + sessionTimeout: 45000, // allows slow handlers + heartbeatInterval: 3000, + }) + + // Lifecycle logs (important) + consumer.on(consumer.events.GROUP_JOIN, (e) => { + logger.info(`Kafka Consumer: Group join – partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) + }) + + consumer.on(consumer.events.REBALANCING, (e) => { + logger.warn(`Kafka Consumer: Rebalancing triggered – ${e.payload.reason}`) + }) + + consumer.on(consumer.events.HEARTBEAT, () => { + logger.debug('Kafka Consumer: Heartbeat OK') + }) await consumer.connect() - await consumer.subscribe({ topics: [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) + logger.info('Kafka Consumer: Connected to broker') + + const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] + + await consumer.subscribe({ topics }) + logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) + //------------------------------------------------------------------ + // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) + //------------------------------------------------------------------ await consumer.run({ - eachMessage: async ({ topic, partition, message }) => { - try { + autoCommit: true, // safe because processing is fast per message + eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { + logger.info( + `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` + ) + + for (const message of batch.messages) { + if (!isRunning() || isStale()) { + logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') + break + } + const rawValue = message.value?.toString() const offset = message.offset + const topic = batch.topic + const partition = batch.partition + + logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) + if (!rawValue) { - logger.warn(`Empty Kafka message skipped on topic ${topic}`) - return + logger.warn(`Kafka Batch: Empty message skipped`) + resolveOffset(offset) + continue } let payload try { payload = JSON.parse(rawValue) } catch (e) { - logger.warn('Invalid JSON in Kafka message; skipping', { - topic, - partition, + logger.warn('Kafka Batch: Invalid JSON, skipping', { offset, - err: e?.message, + err: e.message, }) - return + resolveOffset(offset) + continue } + //-------------------------------------------------------- + // ROUTE MESSAGE TO CORRECT HANDLER + //-------------------------------------------------------- let response - if (payload && topic === process.env.EVENTS_TOPIC) { - // Handle organization events - if ( - payload.entity === 'organization' && - (payload.eventType === 'create' || - payload.eventType === 'update' || - payload.eventType === 'deactivate') - ) { - response = await organizationConsumer.messageReceived(payload) - } - if (payload.entity === 'user') { - if (payload.eventType === 'roleChange') { - response = await rolechangeConsumer.messageReceived(payload) - } - if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) - } - if (payload.eventType === 'delete') { - response = await deleteuserConsumer.messageReceived(payload) + + try { + if (topic === process.env.EVENTS_TOPIC && payload) { + // Handle organization events + if ( + payload.entity === 'organization' && + (payload.eventType === 'create' || + payload.eventType === 'update' || + payload.eventType === 'deactivate') + ) { + response = await organizationConsumer.messageReceived(payload) } - if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { - response = await updateuserConsumer.messageReceived(payload) + + // Handle user events + if (payload.entity === 'user') { + if (payload.eventType === 'roleChange') { + response = await rolechangeConsumer.messageReceived(payload) + } + + if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { + response = await createuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'delete') { + response = await deleteuserConsumer.messageReceived(payload) + } + + if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { + response = await updateuserConsumer.messageReceived(payload) + } } - } - } - if (payload && topic === process.env.CLEAR_INTERNAL_CACHE) { - if (payload.type === 'CLEAR_INTERNAL_CACHE') { + } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { response = await utils.internalDel(payload.value) } + + logger.info(`Kafka event handling response: ${response}`) + } catch (handlerErr) { + logger.error(`Kafka Batch: Error handling message`, { + topic, + partition, + offset, + err: handlerErr.stack || handlerErr.message, + }) } - logger.info(`Kafk event handling response : ${response}`) - } catch (err) { - logger.error(`Error in Kafka message handler for topic ${topic}`, { - topic, - partition, - offset, - err: err?.stack || err?.message || String(err), - }) + + //-------------------------------------------------------- + // MARK MESSAGE AS PROCESSED + //-------------------------------------------------------- + resolveOffset(offset) + + //-------------------------------------------------------- + // HEARTBEAT TO PREVENT TIMEOUT + //-------------------------------------------------------- + await heartbeat() } + + // commit offsets once per batch + await commitOffsetsIfNecessary() }, }) } From 9db429f13b77e7c96ef6af72698aa9208ec4a64d Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 16:23:02 +0530 Subject: [PATCH 43/66] addressed pr comments --- .circleci/config.yml | 2 +- ...nfig.json => integration_test.config.json} | 0 dev-ops/integration_test.mentoring.env | 159 ++++----------- dev-ops/integration_test.user.env | 191 ++++++------------ src/scripts/run-migrations.js | 30 --- src/scripts/run-seeders.js | 30 --- src/start.sh | 14 -- 7 files changed, 101 insertions(+), 325 deletions(-) rename dev-ops/{sample_config.json => integration_test.config.json} (100%) delete mode 100644 src/scripts/run-migrations.js delete mode 100644 src/scripts/run-seeders.js delete mode 100755 src/start.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 58e2e7c6b..6b2b4bff3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,7 +92,7 @@ jobs: # ------------------------------- - run: name: Copy config.json - command: cp dev-ops/sample_config.json src/config.json + command: cp dev-ops/integration_test.config.json src/config.json - run: name: Prepare Kafka config for CI diff --git a/dev-ops/sample_config.json b/dev-ops/integration_test.config.json similarity index 100% rename from dev-ops/sample_config.json rename to dev-ops/integration_test.config.json diff --git a/dev-ops/integration_test.mentoring.env b/dev-ops/integration_test.mentoring.env index 362d61c2a..f7f9cc2b0 100644 --- a/dev-ops/integration_test.mentoring.env +++ b/dev-ops/integration_test.mentoring.env @@ -1,37 +1,24 @@ +#Token secret to verify the access token +ACCESS_TOKEN_SECRET = 'bsj82AHBxahusub12yexlashsbxAXADHBlaj' # Mentoring Service Config ALLOWED_HOST="*" +APPLICATION_ENV = development +APPLICATION_HOST="localhost" # Port on which service runs APPLICATION_PORT = 3000 -APPLICATION_HOST="localhost" -#Service environment -APPLICATION_ENV = development - -#Route after base url -APPLICATION_BASE_URL = /mentoring/ - -#Mongo db connectivity url -MONGODB_URL = mongodb://localhost:27017/mentoring-local - -#Token secret to verify the access token -ACCESS_TOKEN_SECRET = 'bsj82AHBxahusub12yexlashsbxAXADHBlaj' +# Big blue button base url +BIB_BLUE_BUTTON_BASE_URL = /bigbluebutton/ -# Internal access token for communicationcation between services via network call -INTERNAL_ACCESS_TOKEN = 'internal_access_token' +# Big blue button secret key +BIG_BLUE_BUTTON_SECRET_KEY = n -# Kafka hosted server url -KAFKA_URL = kafka:9092 +BIG_BLUE_BUTTON_SESSION_END_URL = 'session_end_url' -# Kafka group to which consumer belongs -KAFKA_GROUP_ID = qa.mentoring -KAFKA_TOPIC="qa.topic" -# Kafka topic to push notification data -NOTIFICATION_KAFKA_TOPIC = 'testTopic' - -# Kafka topic name to consume from mentoring topic -KAFKA_MENTORING_TOPIC ="mentoringtopic" +# Big blue button url +BIG_BLUE_BUTTON_URL = https://dev.mentoring.shikshalokam.org -# Kafka topic to push recording data -KAFKA_RECORDING_TOPIC ="recordingtopic" +#######CACHE_ENABLED = false +#######CACHE_SHARDS= 32 # Any one of three features available for cloud storage CLOUD_STORAGE = 'GCP/AWS/AZURE' @@ -41,118 +28,44 @@ CLOUD_STORAGE_BUCKET_TYPE = '****' CLOUD_STORAGE_PROJECT = "CLOUD_STORAGE_PROJECT" CLOUD_STORAGE_PROVIDER = "gcloud" CLOUD_STORAGE_SECRET = "CLOUD_STORAGE_SECRET" -COMMUNICATION_SERVICE_HOST="http://localhost:3123" -# Gcp json config file path -GCP_PATH = 'gcp.json' - -# Gcp bucket name which stores files -DEFAULT_GCP_BUCKET_NAME = 'gcp-bucket-storage-name' - -# Gcp project id -GCP_PROJECT_ID = 'project-id' - -# Aws access key id -AWS_ACCESS_KEY_ID = 'aws-access-key-id' - -# Aws secret access key -AWS_SECRET_ACCESS_KEY = 'aws-secret-access-key' - -# Aws region where bucket will be located -AWS_BUCKET_REGION = 'ap-south-1' - -# Aws end point -AWS_BUCKET_ENDPOINT = 's3.ap-south-1.amazonaws.com' -# Aws bucket name which stores files -DEFAULT_AWS_BUCKET_NAME = 'aws-bucket-storage-name' - -# Azure storage account name -AZURE_ACCOUNT_NAME = 'account-name' - -# Azure storage account key -AZURE_ACCOUNT_KEY = 'azure-account-key' - -# Azure storage container which stores files -DEFAULT_AZURE_CONTAINER_NAME = 'azure-container-storage-name' - -#user serice host -USER_SERIVCE_HOST = 'http://localhost:3001' - -#user serice base url -USER_SERIVCE_BASE_URL = '/user/' - -# Big blue button url -BIG_BLUE_BUTTON_URL = https://dev.mentoring.shikshalokam.org - -# Big blue button base url -BIB_BLUE_BUTTON_BASE_URL = /bigbluebutton/ -# Meeting end callback events end point -MEETING_END_CALLBACK_EVENTS = https%3A%2F%2Fdev.elevate-apis.shikshalokam.org%2Fmentoring%2Fv1%2Fsessions%2Fcompleted - -# Big blue button secret key -BIG_BLUE_BUTTON_SECRET_KEY = n - -# Big blue button recording ready callback url -RECORDING_READY_CALLBACK_URL = http%3A%2F%2Flocalhost%3A3000%2F%3FmeetingID%3Dmeet123 - -#Enable logging of network request -ENABLE_LOG = true - - -# Api doc url -API_DOC_URL = '/api-doc' - -#Internal cache expiry time -INTERNAL_CACHE_EXP_TIME = 86400 - -# Redis Host connectivity url -REDIS_HOST = 'redis://redis:6379' - -#Kafka internal communicationcation -CLEAR_INTERNAL_CACHE = 'mentoringInternal' - -#Enable email for reported issue. -ENABLE_EMAIL_FOR_REPORT_ISSUE = true - -#Email id of the support team. -SUPPORT_EMAIL_ID = 'support@xyz.com,team@xyz.com' - -#Email template code for reported issue. -REPORT_ISSUE_EMAIL_TEMPLATE_CODE = 'user_issue_reported' - -BIG_BLUE_BUTTON_SESSION_END_URL = 'somethi' - -ERROR_LOG_LEVEL='silly' -DISABLE_LOG=false +COMMUNICATION_SERVICE_HOST="http://localhost:3123" DEFAULT_MEETING_SERVICE="BBB" -SESSION_EDIT_WINDOW_MINUTES=0 -SESSION_MENTEE_LIMIT=0 +DEFAULT_ORGANISATION_CODE="default_code" +DEFAULT_ORGANIZATION_CODE="default_code" DEFAULT_ORG_ID=1 -DISABLE_SESSION_VALIDATION = "true" -SESSION_VERIFICATION_METHOD = "disabled" +DEFAULT_TENANT_CODE="default" +DEV_DATABASE_URL=postgres://postgres:postgres@citus:5432/elevate-mentoring EMAIL_ID_ENCRYPTION_IV="c9c7bd480494409071847264652f5c95" EMAIL_ID_ENCRYPTION_KEY="eef7e009626c18724be86afa41a2620e0718561a508c61f92d7ee0377177ef7b" ENABLE_CHAT="false" +EVENTS_TOPIC="dev.userCreate" +# Internal access token for communicationcation between services via network call +INTERNAL_ACCESS_TOKEN = 'internal_access_token' IS_AUTH_TOKEN_BEARER="false" +# Kafka group to which consumer belongs +KAFKA_GROUP_ID = qa.mentoring +KAFKA_TOPIC="qa.topic" +# Kafka hosted server url +KAFKA_URL = kafka:9092 +LIMIT_FOR_SESSION_REQUEST_MONTH = 1 +# Meeting end callback events end point +MEETING_END_CALLBACK_EVENTS = https%3A%2F%2Fdev.elevate-apis.shikshalokam.org%2Fmentoring%2Fv1%2Fsessions%2Fcompleted MENTOR_ACCEPT_SESSION_REQUEST_EMAIL_TEMPLATE="request_session_accepted_email_template" MENTOR_REJECT_SESSION_REQUEST_EMAIL_TEMPLATE="request_session_rejected_email_template" -MENTOR_PRIVATE_SESSION_INVITE_BY_MANAGER_EMAIL_TEMPLATE="mentor_invite_private_session_by_manager" -MENTOR_PUBLIC_SESSION_INVITE_BY_MANAGER_EMAIL_TEMPLATE="mentor_invite_public_session_by_manager" +# Kafka topic to push notification data +NOTIFICATION_KAFKA_TOPIC = 'testTopic' PORTAL_BASE_URL = "PORTAL_BASE_URL" PUBLIC_ASSET_BUCKETNAME="PUBLIC_ASSET_BUCKETNAME" RATING_KAFKA_TOPIC="RATING_KAFKA_TOPIC" +# Big blue button recording ready callback url +RECORDING_READY_CALLBACK_URL = http%3A%2F%2Flocalhost%3A3000%2F%3FmeetingID%3Dmeet123 +# Redis Host connectivity url +REDIS_HOST = 'redis://redis:6379' SCHEDULER_SERVICE_ERROR_REPORTING_EMAIL_ID="rakesh.k@pacewisdom.com" SCHEDULER_SERVICE_HOST="SCHEDULER_SERVICE_HOST" -SCHEDULER_SERVICE_BASE_URL = "/scheduler/" SCHEDULER_SERVICE_URL = "SCHEDULER_SERVICE_URL" SESSION_MENTEE_LIMIT = "5" SUPPORT_EMAIL_ID="support@shikshalokam.org" -USER_SERVICE_HOST="http://user:3001" -USER_SERVICE_BASE_URL="/user/" -DEFAULT_TENANT_CODE="default" -DEFAULT_ORGANIZATION_CODE="default_code" -AUTH_METHOD="native" - -EVENTS_TOPIC="dev.userCreate" -MONGODB_URL=mongodb://mongo:27017/mentoring-local +USER_SERVICE_HOST="http://user:3001" \ No newline at end of file diff --git a/dev-ops/integration_test.user.env b/dev-ops/integration_test.user.env index 664043d43..c3044ea9b 100644 --- a/dev-ops/integration_test.user.env +++ b/dev-ops/integration_test.user.env @@ -1,129 +1,21 @@ -#User Service Config - -# Port on which service runs -APPLICATION_PORT = 3001 - -# Service environment -APPLICATION_ENV = development - -# Database connectivity url -MONGODB_URL = mongodb://mongo:27017/elevate-mentoring - # Token secret to generate access token ACCESS_TOKEN_SECRET = 'bsj82AHBxahusub12yexlashsbxAXADHBlaj' - -# Token secret to generate refresh token -REFRESH_TOKEN_SECRET = 'refresh-token-secret' - -# Kafka hosted server url -KAFKA_URL = kafka:9092 - -# Kafka group to which consumer belongs -KAFKA_GROUP_ID = qa.users - -# Kafka topic to consume data from -KAFKA_TOPIC = 'qa.topic' - -# Kafka topic to push notification data -NOTIFICATION_KAFKA_TOPIC = 'testTopic' - -# Any one of three features available for cloud storage -CLOUD_STORAGE = 'AWS' - -# Gcp json config file path -GCP_PATH = 'gcp.json' - -# Gcp bucket name which stores files -DEFAULT_GCP_BUCKET_NAME = 'gcp-bucket-storage-name' - -# Gcp project id -GCP_PROJECT_ID = 'project-id' - -# Aws access key id -AWS_ACCESS_KEY_ID = 'aws-access-key-id' - -# Aws secret access key -AWS_SECRET_ACCESS_KEY = 'aws-secret-access-key' - -# Aws region where bucket will be located -AWS_BUCKET_REGION = 'ap-south-1' - -# Aws end point -AWS_BUCKET_ENDPOINT = 's3.ap-south-1.amazonaws.com' - -# Aws bucket name which stores files -DEFAULT_AWS_BUCKET_NAME = 'aws-bucket-storage-name' - -# Azure storage account name -AZURE_ACCOUNT_NAME = 'account-name' - -# Azure storage account key -AZURE_ACCOUNT_KEY = 'azure-account-key' - -# Azure storage container which stores files -DEFAULT_AZURE_CONTAINER_NAME = 'azure-container-storage-name' - -# Internal access token for communicationcation between services via network call -INTERNAL_ACCESS_TOKEN = 'internal_access_token' - -#Enable logging of network request -ENABLE_LOG = true - -# JWT Access Token expiry In Days -ACCESS_TOKEN_EXPIRY = '1' - -# JWT Refresh Token expiry In Days -REFRESH_TOKEN_EXPIRY = '183' - -# Redis Host connectivity url -REDIS_HOST = 'redis://redis:6379' - -# Otp expiration time for forgetpassword or registration process -OTP_EXP_TIME = 86400 - -# Enable email based otp verification for registration process -ENABLE_EMAIL_OTP_VERIFICATION = false +ADMIN_INVITEE_UPLOAD_EMAIL_TEMPLATE_CODE +ADMIN_SECRET_CODE +ALLOWED_HOST = '*' # Api doc url API_DOC_URL = '/api-doc' +APPLICATION_BASE_URL = "/user" -#Enable email for reported issue. -ENABLE_EMAIL_FOR_REPORT_ISSUE = true - -#Email id of the support team. -SUPPORT_EMAIL_ID = 'support@xyz.com,team@xyz.com' - -#Email template code for reported issue. -REPORT_ISSUE_EMAIL_TEMPLATE_CODE = 'user_issue_reported' - -#Internal cache expiry time -INTERNAL_CACHE_EXP_TIME = 86400 - -#Kafka internal communicationcation -CLEAR_INTERNAL_CACHE = 'userInternal' - -APP_NAME = 'user' - -REGISTRATION_EMAIL_TEMPLATE_CODE = 'code' - -OTP_EMAIL_TEMPLATE_CODE = 'ssss' -REDIS_CACHE_EXP_TIME = 11 - -KEY="g5MQ7HG/r5gPCPQQCwfBBEduAt72ewJIY/gWc0RNoak=" -IV="2lIctRkqzYMWbwlW1jCC9A==" - -ERROR_LOG_LEVEL='silly' -DISABLE_LOG=false -SESSION_MENTEE_LIMIT=0 - +# Service environment +APPLICATION_ENV = development APPLICATION_HOST="localhost" -MENTORING_SERVICE_URL="http://localhost:3000" -PORTAL_URL="PORTAL_URL" -EMAIL_ID_ENCRYPTION_IV="c9c7bd480494409071847264652f5c95" -EMAIL_ID_ENCRYPTION_KEY="eef7e009626c18724be86afa41a2620e0718561a508c61f92d7ee0377177ef7b" -EVENT_ORG_LISTENER_URLS="http://localhost:3000/mentoring/v1/organization/eventListener" +# Port on which service runs +APPLICATION_PORT = 3001 +APP_NAME = 'user' CAPTCHA_ENABLE=false -RECAPTCHA_SECRET_KEY=RECAPTCHA_SECRET_KEY + CLOUD_STORAGE = 'GCP/AWS/AZURE' CLOUD_STORAGE_ACCOUNTNAME = '****' CLOUD_STORAGE_BUCKETNAME = '****' @@ -131,16 +23,61 @@ CLOUD_STORAGE_BUCKET_TYPE = '****' CLOUD_STORAGE_PROJECT = "CLOUD_STORAGE_PROJECT" CLOUD_STORAGE_PROVIDER = "gcloud" CLOUD_STORAGE_SECRET = "CLOUD_STORAGE_SECRET" -ENTITY_MANAGEMENT_SERVICE_BASE_URL="http://localhost:3000/mentoring/" -PUBLIC_ASSET_BUCKETNAME=PUBLIC_ASSET_BUCKETNAME -EVENT_USER_KAFKA_TOPIC=dev.userCreate -SIGNED_URL_EXPIRY_IN_SECONDS="900" -ACCESS_TOKEN_EXPIRY="4320m" -REFRESH_TOKEN_EXPIRY="7" +DEFAULT_ORGANISATION_CODE = "default_code" + +DEFAULT_ORG_ID=1 DEFAULT_ROLE="mentor,mentee" DEFAULT_TENANT_ORG_CODE="default_code" DEFAULT_TENANT_ORG_NAME="Default Organization" -DEFAULT_ORG_ID=1 -DEFAULT_ORGANISATION_CODE="default_code" -IS_AUTH_TOKEN_BEARER=false \ No newline at end of file +DEV_DATABASE_URL=postgres://postgres:postgres@citus:5432/mentoring_user +EMAIL_ID_ENCRYPTION_IV="c9c7bd480494409071847264652f5c95" +EMAIL_ID_ENCRYPTION_KEY="eef7e009626c18724be86afa41a2620e0718561a508c61f92d7ee0377177ef7b" +ENTITY_MANAGEMENT_SERVICE_BASE_URL="http://localhost:3000/mentoring/" +EVENT_ENABLE_ORG_KAFKA_EVENTS = true +EVENT_ENABLE_TENANT_KAFKA_EVENTS = true +EVENT_ENABLE_USER_KAFKA_EVENTS = true +EVENT_ORGANIZATION_KAFKA_TOPIC = "dev.organizationEvent" +EVENT_ORG_LISTENER_URLS="http://localhost:3000/mentoring/v1/organization/eventListener" +EVENT_TENANT_KAFKA_TOPIC="dev.tenantEvent" +EVENT_USER_KAFKA_TOPIC="dev.userCreate" +# Internal access token for communicationcation between services via network call +INTERNAL_ACCESS_TOKEN = 'internal_access_token' +#Internal cache expiry time +INTERNAL_CACHE_EXP_TIME = 86400 +INVITEE_EMAIL_TEMPLATE_CODE="invite_user" +IS_AUTH_TOKEN_BEARER=false +IV="2lIctRkqzYMWbwlW1jCC9A==" +# Kafka group to which consumer belongs +KAFKA_GROUP_ID = qa.users +# Kafka topic to consume data from +KAFKA_TOPIC = 'qa.topic' +# Kafka hosted server url +KAFKA_URL = kafka:9092 +KEY="g5MQ7HG/r5gPCPQQCwfBBEduAt72ewJIY/gWc0RNoak=" +MENTORING_SERVICE_URL="http://localhost:3000" +# Kafka topic to push notification data +NOTIFICATION_KAFKA_TOPIC = 'testTopic' +OTP_EMAIL_TEMPLATE_CODE = 'ssss' +# Otp expiration time for forgetpassword or registration process +OTP_EXP_TIME = 86400 +PASSWORD_POLICY_MESSAGE = "Password must have at least two uppercase letters, two numbers, three special characters, and be at least 11 characters long." +PORTAL_URL="PORTAL_URL" +PUBLIC_ASSET_BUCKETNAME=PUBLIC_ASSET_BUCKETNAME +RATING_KAFKA_TOPIC = "dev.mentor_rating" +RECAPTCHA_SECRET_KEY=RECAPTCHA_SECRET_KEY +REDIS_HOST = 'redis://redis:6379' +REFRESH_TOKEN_EXPIRY="7" +# Token secret to generate refresh token +REFRESH_TOKEN_SECRET = 'refresh-token-secret' +REFRESH_VIEW_INTERVAL= 30000 +REGISTRATION_EMAIL_TEMPLATE_CODE = 'code' +REGISTRATION_OTP_EMAIL_TEMPLATE_CODE= "registrationotp" +SALT_ROUNDS = "10" +SAMPLE_CSV_FILE_PATH="sample/bulk_user_creation.csv" +SCHEDULER_SERVICE_BASE_URL="/scheduler/" +SCHEDULER_SERVICE_ERROR_REPORTING_EMAIL_ID = "rakesh.k@pacewisdom.com" +SCHEDULER_SERVICE_HOST="http://localhost:3567" +SCHEDULER_SERVICE_URL="http://localhost:3567/jobs/scheduleJob" +SERVICE_NAME = "UserService" +SIGNED_URL_EXPIRY_IN_SECONDS = "900" \ No newline at end of file diff --git a/src/scripts/run-migrations.js b/src/scripts/run-migrations.js deleted file mode 100644 index ffc83d615..000000000 --- a/src/scripts/run-migrations.js +++ /dev/null @@ -1,30 +0,0 @@ -const Umzug = require('umzug') -const path = require('path') -const { sequelize } = require('../database/models/index') // adjust path if needed - -async function runMigrations() { - const umzug = new Umzug({ - storage: 'sequelize', - storageOptions: { - sequelize, - }, - migrations: { - path: path.join(__dirname, '../database/migrations'), - pattern: /\.js$/, - params: [sequelize.getQueryInterface(), sequelize.constructor], - }, - logging: console.log, - }) - - try { - console.log('Starting migrations...') - await umzug.up() - console.log('MIGRATION_SUCCESS') - process.exit(0) - } catch (err) { - console.error('Migration failed:', err) - process.exit(1) - } -} - -runMigrations() diff --git a/src/scripts/run-seeders.js b/src/scripts/run-seeders.js deleted file mode 100644 index 7a8a588c8..000000000 --- a/src/scripts/run-seeders.js +++ /dev/null @@ -1,30 +0,0 @@ -const Umzug = require('umzug') -const path = require('path') -const { sequelize } = require('../database/models/index') // adjust path if needed - -async function runSeeders() { - const umzug = new Umzug({ - storage: 'sequelize', - storageOptions: { - sequelize, - }, - migrations: { - path: path.join(__dirname, '../database/seeders'), - pattern: /\.js$/, - params: [sequelize.getQueryInterface(), sequelize.constructor], - }, - logging: console.log, - }) - - try { - console.log('Running seeders...') - await umzug.up() // <-- BLOCKS until all seeds are done - console.log('SEEDER_SUCCESS') - process.exit(0) - } catch (err) { - console.error('Seeding failed:', err) - process.exit(1) - } -} - -runSeeders() diff --git a/src/start.sh b/src/start.sh deleted file mode 100755 index cc4019deb..000000000 --- a/src/start.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -set -e - -# Replace kafka.js with kafka.ci.js -cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js - -echo "[MENTORING] Running migrations..." -npm run db:init:integration - -echo "[MENTORING] Running seeds..." -npm run db:seed:all:integration - -echo "[MENTORING] Starting application..." -npm run dev From 42b28e6c3bb860606e1fe5b14987d5d7fd8cfac4 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 16:32:02 +0530 Subject: [PATCH 44/66] removed unused variables --- src/integration-tests-new/admin/admin.spec.js | 1 - .../cloud-services/cloud-services.spec.js | 2 -- src/integration-tests-new/config/config.spec.js | 2 -- src/integration-tests-new/connections/connections.specs.js | 3 +-- src/integration-tests-new/entity-type/entity-type.specs.js | 1 - src/integration-tests-new/entity/entity.specs.js | 1 - src/integration-tests-new/feedback/feedback.spec.js | 2 -- src/integration-tests-new/issues/issues.spec.js | 2 -- src/integration-tests-new/mentees/mentees.specs.js | 1 - src/integration-tests-new/mentoring/mentoring.spec.js | 2 -- src/integration-tests-new/mentors/mentors.specs.js | 1 - src/integration-tests-new/modules/modules.spec.js | 2 -- src/integration-tests-new/org-admin/org-admin.spec.js | 2 -- src/integration-tests-new/permissions/permissions.spec.js | 2 -- src/integration-tests-new/profile/profile.specs.js | 1 - src/integration-tests-new/question-set/question-set.spec.js | 2 -- src/integration-tests-new/questions/questions.spec.js | 2 -- .../report-mapping/report-mapping.spec.js | 2 -- .../report-queries/report-queries.spec.js | 2 -- src/integration-tests-new/report-type/report-type.spec.js | 2 -- src/integration-tests-new/reports/reports.spec.js | 2 -- .../requestSessions/requestSessions.specs.js | 1 - .../role-extension/role-extension.spec.js | 2 -- .../rolePermissionMapping/rolePermissionMapping.spec.js | 2 -- src/integration-tests-new/sessions/sessions.specs.js | 1 - src/integration-tests-new/users/users.spec.js | 2 -- 26 files changed, 1 insertion(+), 44 deletions(-) diff --git a/src/integration-tests-new/admin/admin.spec.js b/src/integration-tests-new/admin/admin.spec.js index d899b5c24..db5a4e010 100644 --- a/src/integration-tests-new/admin/admin.spec.js +++ b/src/integration-tests-new/admin/admin.spec.js @@ -1,6 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const schemas = require('./schemas/admin.schemas.json') describe('admin endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/cloud-services/cloud-services.spec.js b/src/integration-tests-new/cloud-services/cloud-services.spec.js index 770a8ab36..90237c5f5 100644 --- a/src/integration-tests-new/cloud-services/cloud-services.spec.js +++ b/src/integration-tests-new/cloud-services/cloud-services.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/cloud-services.schemas.json') describe('cloud-services endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/config/config.spec.js b/src/integration-tests-new/config/config.spec.js index 9b8d71c28..072c42e37 100644 --- a/src/integration-tests-new/config/config.spec.js +++ b/src/integration-tests-new/config/config.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/config.schemas.json') describe('config endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/connections/connections.specs.js b/src/integration-tests-new/connections/connections.specs.js index d7325eb67..20fc9540e 100644 --- a/src/integration-tests-new/connections/connections.specs.js +++ b/src/integration-tests-new/connections/connections.specs.js @@ -1,13 +1,12 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') -let adminDetails = null const schemas = require('./schemas/connections.schemas.json') beforeAll(async () => { + console.log('Attempting to login...') adminDetails = await commonHelper.adminLogin() }) diff --git a/src/integration-tests-new/entity-type/entity-type.specs.js b/src/integration-tests-new/entity-type/entity-type.specs.js index d8d7f30bc..9495013ee 100644 --- a/src/integration-tests-new/entity-type/entity-type.specs.js +++ b/src/integration-tests-new/entity-type/entity-type.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let menteeDetails = null // This user will make the request let mentorDetails = null // This user will be the requestee diff --git a/src/integration-tests-new/entity/entity.specs.js b/src/integration-tests-new/entity/entity.specs.js index 70016a6c2..ea99229af 100644 --- a/src/integration-tests-new/entity/entity.specs.js +++ b/src/integration-tests-new/entity/entity.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let menteeDetails = null let mentorDetails = null diff --git a/src/integration-tests-new/feedback/feedback.spec.js b/src/integration-tests-new/feedback/feedback.spec.js index bcce44b56..a2d3848e0 100644 --- a/src/integration-tests-new/feedback/feedback.spec.js +++ b/src/integration-tests-new/feedback/feedback.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/feedback.schemas.json') describe('feedback endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/issues/issues.spec.js b/src/integration-tests-new/issues/issues.spec.js index 2e2429ba9..4e4b41e11 100644 --- a/src/integration-tests-new/issues/issues.spec.js +++ b/src/integration-tests-new/issues/issues.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/issues.schemas.json') describe('issues endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/mentees/mentees.specs.js b/src/integration-tests-new/mentees/mentees.specs.js index d3997b0e6..dba594db1 100644 --- a/src/integration-tests-new/mentees/mentees.specs.js +++ b/src/integration-tests-new/mentees/mentees.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') const schemas = require('./schemas/mentees.schemas.json') let userDetails = null diff --git a/src/integration-tests-new/mentoring/mentoring.spec.js b/src/integration-tests-new/mentoring/mentoring.spec.js index 2728105ed..c81d747c7 100644 --- a/src/integration-tests-new/mentoring/mentoring.spec.js +++ b/src/integration-tests-new/mentoring/mentoring.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/mentoring.schemas.json') describe('mentoring endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/mentors/mentors.specs.js b/src/integration-tests-new/mentors/mentors.specs.js index f7ea210a1..371e0dfc6 100644 --- a/src/integration-tests-new/mentors/mentors.specs.js +++ b/src/integration-tests-new/mentors/mentors.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let userDetails = null const schemas = require('./schemas/mentors.schemas.json') diff --git a/src/integration-tests-new/modules/modules.spec.js b/src/integration-tests-new/modules/modules.spec.js index 12496e575..7b1bc18f6 100644 --- a/src/integration-tests-new/modules/modules.spec.js +++ b/src/integration-tests-new/modules/modules.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/modules.schemas.json') describe('modules endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/org-admin/org-admin.spec.js b/src/integration-tests-new/org-admin/org-admin.spec.js index a192c01d5..9525f65ec 100644 --- a/src/integration-tests-new/org-admin/org-admin.spec.js +++ b/src/integration-tests-new/org-admin/org-admin.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/org-admin.schemas.json') describe('org-admin endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/permissions/permissions.spec.js b/src/integration-tests-new/permissions/permissions.spec.js index e066740e1..1819280a3 100644 --- a/src/integration-tests-new/permissions/permissions.spec.js +++ b/src/integration-tests-new/permissions/permissions.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/permissions.schemas.json') describe('permissions endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/profile/profile.specs.js b/src/integration-tests-new/profile/profile.specs.js index d8ddfc159..58e685fec 100644 --- a/src/integration-tests-new/profile/profile.specs.js +++ b/src/integration-tests-new/profile/profile.specs.js @@ -3,7 +3,6 @@ const request = require('supertest') const fs = require('fs') const path = require('path') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let mentorDetails = null const schemas = require('./schemas/profile.schemas.json') diff --git a/src/integration-tests-new/question-set/question-set.spec.js b/src/integration-tests-new/question-set/question-set.spec.js index c227c972a..4f2606b45 100644 --- a/src/integration-tests-new/question-set/question-set.spec.js +++ b/src/integration-tests-new/question-set/question-set.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/question-set.schemas.json') describe('question-set endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/questions/questions.spec.js b/src/integration-tests-new/questions/questions.spec.js index 162069fa9..42bd588a8 100644 --- a/src/integration-tests-new/questions/questions.spec.js +++ b/src/integration-tests-new/questions/questions.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/questions.schemas.json') describe('questions endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/report-mapping/report-mapping.spec.js b/src/integration-tests-new/report-mapping/report-mapping.spec.js index 01a04f8c3..c6f85776c 100644 --- a/src/integration-tests-new/report-mapping/report-mapping.spec.js +++ b/src/integration-tests-new/report-mapping/report-mapping.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/report-mapping.schemas.json') describe('report-mapping endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/report-queries/report-queries.spec.js b/src/integration-tests-new/report-queries/report-queries.spec.js index 169c14bf4..47ac4b8aa 100644 --- a/src/integration-tests-new/report-queries/report-queries.spec.js +++ b/src/integration-tests-new/report-queries/report-queries.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/report-queries.schemas.json') describe('report-queries endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/report-type/report-type.spec.js b/src/integration-tests-new/report-type/report-type.spec.js index 8515f9f8b..2e352cbc1 100644 --- a/src/integration-tests-new/report-type/report-type.spec.js +++ b/src/integration-tests-new/report-type/report-type.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/report-type.schemas.json') describe('report-type endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/reports/reports.spec.js b/src/integration-tests-new/reports/reports.spec.js index cb89a50a3..3673641cc 100644 --- a/src/integration-tests-new/reports/reports.spec.js +++ b/src/integration-tests-new/reports/reports.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/reports.schemas.json') describe('reports endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/requestSessions/requestSessions.specs.js b/src/integration-tests-new/requestSessions/requestSessions.specs.js index 42c701888..fb0e9a51d 100644 --- a/src/integration-tests-new/requestSessions/requestSessions.specs.js +++ b/src/integration-tests-new/requestSessions/requestSessions.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(60000) // Set default timeout to 30 seconds const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let menteeDetails = null // This user will make the request let mentorDetails = null // This user will be the requestee diff --git a/src/integration-tests-new/role-extension/role-extension.spec.js b/src/integration-tests-new/role-extension/role-extension.spec.js index 8663bf67e..055725468 100644 --- a/src/integration-tests-new/role-extension/role-extension.spec.js +++ b/src/integration-tests-new/role-extension/role-extension.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/role-extension.schemas.json') describe('role-extension endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js index d3327a15a..da0a27ceb 100644 --- a/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js +++ b/src/integration-tests-new/rolePermissionMapping/rolePermissionMapping.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/rolePermissionMapping.schemas.json') describe('rolePermissionMapping endpoints generated from api-doc.yaml', () => { diff --git a/src/integration-tests-new/sessions/sessions.specs.js b/src/integration-tests-new/sessions/sessions.specs.js index 3bc4b9d41..2547ddc44 100644 --- a/src/integration-tests-new/sessions/sessions.specs.js +++ b/src/integration-tests-new/sessions/sessions.specs.js @@ -1,7 +1,6 @@ jest.setTimeout(100000) const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' const commonHelper = require('@commonTests') let userDetails = null diff --git a/src/integration-tests-new/users/users.spec.js b/src/integration-tests-new/users/users.spec.js index 8150cc16d..80a14c8b3 100644 --- a/src/integration-tests-new/users/users.spec.js +++ b/src/integration-tests-new/users/users.spec.js @@ -1,7 +1,5 @@ const request = require('supertest') const BASE = process.env.BASE_URL || 'http://localhost:3000' -const TOKEN = process.env.TEST_BEARER_TOKEN || 'test-token' - const schemas = require('./schemas/users.schemas.json') describe('users endpoints generated from api-doc.yaml', () => { From 852b7013858e97f1b7ce67cb8ae8abef6032162e Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 16:51:49 +0530 Subject: [PATCH 45/66] enabling citus --- .circleci/config.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6b2b4bff3..11e314b9d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -134,16 +134,23 @@ jobs: docker ps --format "table {{.Names}}\t{{.Status}}" sleep 5 done + + - run: + name: Enable Citus and run distributionColumns.psql + command: | + cd dev-ops + echo "Enabling Citus extension..." + docker-compose exec citus psql \ + -U postgres \ + -d elevate-mentoring \ + -c "CREATE EXTENSION IF NOT EXISTS citus;" + + echo "Running distributionColumns.psql..." + docker-compose exec citus psql \ + -U postgres \ + -d elevate-mentoring \ + -f /var/src/distributionColumns.psql - # ------------------------------- - # CI-specific Kafka config - # ------------------------------- - # - run: - # name: Configure Kafka for CI - # command: | - # export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - # docker exec "$MENTORING" \ - # sh -c 'cp /var/src/configs/kafka.ci.js /var/src/configs/kafka.js' # ------------------------------- # Run migrations (EXPLICIT) From 189db62b24f770534b1f6d03d3a8ff41c77ced57 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 16:56:59 +0530 Subject: [PATCH 46/66] correcting the citus code --- .circleci/config.yml | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 11e314b9d..cd5dec8ac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -134,24 +134,6 @@ jobs: docker ps --format "table {{.Names}}\t{{.Status}}" sleep 5 done - - - run: - name: Enable Citus and run distributionColumns.psql - command: | - cd dev-ops - echo "Enabling Citus extension..." - docker-compose exec citus psql \ - -U postgres \ - -d elevate-mentoring \ - -c "CREATE EXTENSION IF NOT EXISTS citus;" - - echo "Running distributionColumns.psql..." - docker-compose exec citus psql \ - -U postgres \ - -d elevate-mentoring \ - -f /var/src/distributionColumns.psql - - # ------------------------------- # Run migrations (EXPLICIT) # ------------------------------- @@ -171,7 +153,22 @@ jobs: export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) echo "Running seeders in $MENTORING" docker exec "$MENTORING" npm run db:seed:all + + - run: + name: Enable Citus and run distributionColumns.psql + command: | + cd dev-ops + echo "Enabling Citus extension..." + docker-compose exec citus psql \ + -U postgres \ + -d elevate-mentoring \ + -c "CREATE EXTENSION IF NOT EXISTS citus;" + echo "Running distributionColumns.psql..." + docker-compose exec citus psql \ + -U postgres \ + -d elevate-mentoring \ + -f /var/src/distributionColumns.psql # ------------------------------- # Run integration tests # ------------------------------- From 802744fec31a575c26ea840fcb3cb9f0111599f4 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 17:13:41 +0530 Subject: [PATCH 47/66] enabling citus - 2 --- dev-ops/docker-compose.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 1af83a62e..6a87b899b 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -54,6 +54,8 @@ services: citus: image: citusdata/citus:13.0 + volumes: + - ../src/distributionColumns.psql:/var/src/distributionColumns.psql container_name: citus_db ports: - '5432:5432' @@ -77,7 +79,11 @@ services: ports: - '3000:3000' - '9229:9229' - command: ["npm", "run", "dev"] + command: > + sh -c "sleep 20 && + npm run db:init && + npm run db:seed:all && + node --trace-warnings app.js" environment: - KAFKA_URL=kafka:9092 From 1f53832d7e5d086208c3b8fd3c689d4a253c9d68 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 17:40:43 +0530 Subject: [PATCH 48/66] enabling citus - 3 --- dev-ops/docker-compose.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 6a87b899b..36413f627 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -54,17 +54,22 @@ services: citus: image: citusdata/citus:13.0 - volumes: - - ../src/distributionColumns.psql:/var/src/distributionColumns.psql container_name: citus_db + + volumes: + - ../src/distributionColumns.psql:/var/src/distributionColumns.psql + ports: - '5432:5432' + environment: - POSTGRES_USER: 'postgres' - POSTGRES_PASSWORD: 'postgres' - POSTGRES_HOST_AUTH_METHOD: 'trust' + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + networks: - elevate_net + healthcheck: test: ['CMD-SHELL', 'pg_isready -U postgres'] interval: 10s From 200164e384d9b9f3406f5e2976c254103801847c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 17:53:01 +0530 Subject: [PATCH 49/66] enabling citus - 4 --- dev-ops/docker-compose.yml | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 36413f627..1af83a62e 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -55,21 +55,14 @@ services: citus: image: citusdata/citus:13.0 container_name: citus_db - - volumes: - - ../src/distributionColumns.psql:/var/src/distributionColumns.psql - ports: - '5432:5432' - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_HOST_AUTH_METHOD: trust - + POSTGRES_USER: 'postgres' + POSTGRES_PASSWORD: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' networks: - elevate_net - healthcheck: test: ['CMD-SHELL', 'pg_isready -U postgres'] interval: 10s @@ -84,11 +77,7 @@ services: ports: - '3000:3000' - '9229:9229' - command: > - sh -c "sleep 20 && - npm run db:init && - npm run db:seed:all && - node --trace-warnings app.js" + command: ["npm", "run", "dev"] environment: - KAFKA_URL=kafka:9092 From 28b67e679271196e32530ab4d0e61e9c37ee7a09 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 17:57:18 +0530 Subject: [PATCH 50/66] enabling citus - 5 --- dev-ops/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 1af83a62e..3852456f9 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -55,6 +55,8 @@ services: citus: image: citusdata/citus:13.0 container_name: citus_db + volumes: + - ../src/distributionColumns.psql:/var/src/distributionColumns.psql ports: - '5432:5432' environment: From 8ccbf94932d71591b44cbf1560e746c52fe7b05c Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 13 Jan 2026 18:11:56 +0530 Subject: [PATCH 51/66] enabling citus - 6 --- .circleci/config.yml | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cd5dec8ac..ddab18c82 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -153,22 +153,27 @@ jobs: export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) echo "Running seeders in $MENTORING" docker exec "$MENTORING" npm run db:seed:all - + - run: - name: Enable Citus and run distributionColumns.psql - command: | - cd dev-ops - echo "Enabling Citus extension..." - docker-compose exec citus psql \ - -U postgres \ - -d elevate-mentoring \ - -c "CREATE EXTENSION IF NOT EXISTS citus;" - - echo "Running distributionColumns.psql..." - docker-compose exec citus psql \ - -U postgres \ - -d elevate-mentoring \ - -f /var/src/distributionColumns.psql + name: Enable Citus and run distributionColumns.psql + command: | + cd dev-ops + set -e + + echo "Enabling Citus extension..." + docker-compose exec -T citus psql \ + -U postgres \ + -d elevate-mentoring \ + -c "CREATE EXTENSION IF NOT EXISTS citus;" + + echo "Running distributionColumns.psql..." + docker-compose exec -T citus psql \ + -U postgres \ + -d elevate-mentoring \ + -f /var/src/distributionColumns.psql + + echo "Citus setup completed." + # ------------------------------- # Run integration tests # ------------------------------- From e933acf317f7f84f4037903c539345b26ddcc644 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 10:32:47 +0530 Subject: [PATCH 52/66] citus and distribution placement fixed in circleci --- .circleci/config.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ddab18c82..a4c75f052 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -144,16 +144,6 @@ jobs: echo "Running migrations in $MENTORING" docker exec "$MENTORING" npm run db:init - # ------------------------------- - # Run seeders (EXPLICIT) - # ------------------------------- - - run: - name: Run mentoring seeders - command: | - export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) - echo "Running seeders in $MENTORING" - docker exec "$MENTORING" npm run db:seed:all - - run: name: Enable Citus and run distributionColumns.psql command: | @@ -174,6 +164,16 @@ jobs: echo "Citus setup completed." + # ------------------------------- + # Run seeders (EXPLICIT) + # ------------------------------- + - run: + name: Run mentoring seeders + command: | + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Running seeders in $MENTORING" + docker exec "$MENTORING" npm run db:seed:all + # ------------------------------- # Run integration tests # ------------------------------- From c86582cfa3de59ea490852f0bd9b4efa6b2f2419 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 10:46:21 +0530 Subject: [PATCH 53/66] updated citus config --- src/distributionColumns.psql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/distributionColumns.psql b/src/distributionColumns.psql index e7746237c..026eed134 100644 --- a/src/distributionColumns.psql +++ b/src/distributionColumns.psql @@ -1,3 +1,6 @@ +SELECT create_reference_table('entity_types'); +SELECT create_reference_table('permissions'); +SELECT create_reference_table('sessions'); SELECT create_distributed_table('availabilities', 'tenant_code'); SELECT create_distributed_table('connection_requests', 'tenant_code'); SELECT create_distributed_table('connections', 'tenant_code'); From b4a1fa1af59e64eee6451625d60fe453f0531c9e Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 10:53:09 +0530 Subject: [PATCH 54/66] updated citus config --- src/distributionColumns.psql | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/distributionColumns.psql b/src/distributionColumns.psql index 026eed134..4b5bed3b6 100644 --- a/src/distributionColumns.psql +++ b/src/distributionColumns.psql @@ -6,7 +6,6 @@ SELECT create_distributed_table('connection_requests', 'tenant_code'); SELECT create_distributed_table('connections', 'tenant_code'); SELECT create_distributed_table('default_rules', 'tenant_code'); SELECT create_distributed_table('entities', 'tenant_code'); -SELECT create_distributed_table('entity_types', 'tenant_code'); SELECT create_distributed_table('feedbacks', 'tenant_code'); SELECT create_distributed_table('file_uploads', 'tenant_code'); SELECT create_distributed_table('forms', 'tenant_code'); @@ -24,8 +23,6 @@ SELECT create_distributed_table('resources', 'tenant_code'); SELECT create_distributed_table('role_extensions', 'tenant_code'); SELECT create_distributed_table('session_attendees', 'tenant_code'); SELECT create_distributed_table('session_request', 'tenant_code'); -SELECT create_distributed_table('sessions', 'tenant_code'); SELECT create_distributed_table('user_extensions', 'tenant_code'); -SELECT create_distributed_table('permissions', 'id'); SELECT create_distributed_table('role_permission_mapping', 'role_title'); From ecb4ecb49bfb5de3b9e81d44c543e2ab192afc34 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 15:02:45 +0530 Subject: [PATCH 55/66] circleci and docker-compose file updated --- .circleci/config.yml | 7 +++++++ dev-ops/docker-compose.yml | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a4c75f052..19cf8b053 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,6 +174,13 @@ jobs: echo "Running seeders in $MENTORING" docker exec "$MENTORING" npm run db:seed:all + - run: + name: Start mentoring app + command: | + export MENTORING=$(docker ps --format "{{.Names}}" | grep mentoring) + echo "Starting mentoring app..." + docker exec -d "$MENTORING" npm run dev + # ------------------------------- # Run integration tests # ------------------------------- diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 3852456f9..754beb095 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -79,8 +79,12 @@ services: ports: - '3000:3000' - '9229:9229' - command: ["npm", "run", "dev"] - + # command: > + # sh -c "sleep 20 && + # npm run db:init && + # npm run db:seed:all && + # nodemon --trace-warnings app.js" + #command: ['npm','run','dev'] environment: - KAFKA_URL=kafka:9092 - USER_SERVICE_HOST=http://user:3001 From 5afd2888b6cfa67965fdf5d51c786069b9eda3b1 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 15:30:31 +0530 Subject: [PATCH 56/66] test sequencer fix --- src/integration-tests-new/sessions/sessions.specs.js | 1 - src/integration-tests-new/testSequencer.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/integration-tests-new/sessions/sessions.specs.js b/src/integration-tests-new/sessions/sessions.specs.js index 2547ddc44..7c25535ed 100644 --- a/src/integration-tests-new/sessions/sessions.specs.js +++ b/src/integration-tests-new/sessions/sessions.specs.js @@ -48,7 +48,6 @@ describe('sessions endpoints generated from api-doc.yaml', () => { }) // Assuming 201 is the success status for creation - console.log(createRes, 'create status*') expect(createRes.status).toBe(201) createdSessionId = createRes.body.result.id expect(createdSessionId).toBeDefined() diff --git a/src/integration-tests-new/testSequencer.js b/src/integration-tests-new/testSequencer.js index 9930bf115..eac08c7c6 100644 --- a/src/integration-tests-new/testSequencer.js +++ b/src/integration-tests-new/testSequencer.js @@ -9,11 +9,11 @@ class CustomSequencer extends Sequencer { 'entity/entity.specs.js', 'entity-type/entity-type.specs.js', 'form/form.specs.js', - 'mentees/mentees.specs.js', - 'mentors/mentors.specs.js', 'profile/profile.specs.js', 'requestSessions/requestSessions.specs.js', 'sessions/sessions.specs.js', + 'mentees/mentees.specs.js', + 'mentors/mentors.specs.js', 'default-rule/default-rule.specs.js', ] From e2467358460f0ce84888d5ff13581b8c5f8410a7 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 15:35:33 +0530 Subject: [PATCH 57/66] test sequencer fix -1 --- src/integrationJest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrationJest.config.js b/src/integrationJest.config.js index 11cc5985e..c22961475 100644 --- a/src/integrationJest.config.js +++ b/src/integrationJest.config.js @@ -19,7 +19,7 @@ module.exports = { '@helpers/(.*)': '/helpers/$1', '@utils/(.*)': '/utils/$1', }, - testMatch: ['/integration-tests-new/**/*.specs.js'], + testMatch: ['/integration-tests-new/**/*.specss.js'], testSequencer: '/integration-tests-new/testSequencer', reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: '../dev-ops/report' }]], } From 1ac061d4229f579e2573a555d181830ca27ca40a Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 15:36:47 +0530 Subject: [PATCH 58/66] test sequencer fix -2 --- src/integration-tests-new/testSequencer.js | 4 ++-- src/integrationJest.config.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/integration-tests-new/testSequencer.js b/src/integration-tests-new/testSequencer.js index eac08c7c6..334e662be 100644 --- a/src/integration-tests-new/testSequencer.js +++ b/src/integration-tests-new/testSequencer.js @@ -5,6 +5,8 @@ class CustomSequencer extends Sequencer { sort(tests) { // Define exact execution order (must match actual filenames) const executionOrder = [ + 'mentees/mentees.specs.js', + 'mentors/mentors.specs.js', 'connections/connections.specs.js', 'entity/entity.specs.js', 'entity-type/entity-type.specs.js', @@ -12,8 +14,6 @@ class CustomSequencer extends Sequencer { 'profile/profile.specs.js', 'requestSessions/requestSessions.specs.js', 'sessions/sessions.specs.js', - 'mentees/mentees.specs.js', - 'mentors/mentors.specs.js', 'default-rule/default-rule.specs.js', ] diff --git a/src/integrationJest.config.js b/src/integrationJest.config.js index c22961475..11cc5985e 100644 --- a/src/integrationJest.config.js +++ b/src/integrationJest.config.js @@ -19,7 +19,7 @@ module.exports = { '@helpers/(.*)': '/helpers/$1', '@utils/(.*)': '/utils/$1', }, - testMatch: ['/integration-tests-new/**/*.specss.js'], + testMatch: ['/integration-tests-new/**/*.specs.js'], testSequencer: '/integration-tests-new/testSequencer', reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: '../dev-ops/report' }]], } From 32e1727b66b0ef4797ede589c7629fd521786663 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 14 Jan 2026 16:58:02 +0530 Subject: [PATCH 59/66] starting user service via circleci --- .circleci/config.yml | 22 ++++++++++++++++++++++ dev-ops/docker-compose.yml | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 19cf8b053..ca40717c6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -181,6 +181,28 @@ jobs: echo "Starting mentoring app..." docker exec -d "$MENTORING" npm run dev + - run: + name: Run user migrations + command: | + export USER=$(docker ps --format "{{.Names}}" | grep user) + echo "Running user migrations in $USER" + docker exec "$USER" npm run db:init + + - run: + name: Run user seeders + command: | + export USER=$(docker ps --format "{{.Names}}" | grep user) + docker exec "$USER" npm run db:seed:all + + + - run: + name: Start user service + command: | + export USER=$(docker ps --format "{{.Names}}" | grep user) + echo "Starting user service..." + docker exec -d "$USER" npm run dev + + # ------------------------------- # Run integration tests # ------------------------------- diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index 754beb095..c36ba0571 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -113,11 +113,11 @@ services: ports: - '3001:3001' # - '9229:9229' - command: > - sh -c "sleep 20 && - npm run db:init && - npm run db:seed:all && - nodemon --trace-warnings app.js" + # command: > + # sh -c "sleep 20 && + # npm run db:init && + # npm run db:seed:all && + # nodemon --trace-warnings app.js" environment: - KAFKA_URL=kafka:9092 - REDIS_HOST=redis://redis:6379 From d5eee4961e749ea78a1d65c4c2965d99a5b859b1 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 11:54:19 +0530 Subject: [PATCH 60/66] savepoint-1 refresh view build --- src/integration-tests-new/mentees/mentees.specs.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/integration-tests-new/mentees/mentees.specs.js b/src/integration-tests-new/mentees/mentees.specs.js index dba594db1..76eca77fe 100644 --- a/src/integration-tests-new/mentees/mentees.specs.js +++ b/src/integration-tests-new/mentees/mentees.specs.js @@ -7,6 +7,8 @@ let userDetails = null beforeAll(async () => { console.log('setting up global variables....') + adminDetails = await commonHelper.adminLogin() + let adminToken = adminDetails.token userDetails = await commonHelper.logIn() let profileCreate = await request(BASE) @@ -22,6 +24,8 @@ beforeAll(async () => { external_session_visibility: 'CURRENT', external_mentor_visibility: 'ALL', }) + + await request(BASE).post('/mentoring/v1/admin/triggerViewRebuild').set('x-auth-token', adminToken) }) describe('mentees endpoints generated from api-doc.yaml', () => { From 07f81997d3d26b805e518ba128fbf18962e302a9 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 15:20:55 +0530 Subject: [PATCH 61/66] savepoint-2 minor fixes --- .circleci/config.yml | 2 +- dev-ops/docker-compose.yml | 13 --- src/configs/kafka.ci.js | 173 ------------------------------------- src/configs/kafka.js | 6 +- 4 files changed, 2 insertions(+), 192 deletions(-) delete mode 100644 src/configs/kafka.ci.js diff --git a/.circleci/config.yml b/.circleci/config.yml index ca40717c6..3b5d91838 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,7 +51,7 @@ jobs: command: | cd .. git clone https://github.com/ELEVATE-Project/user.git \ - --branch mentoring_integration_test_setup \ + --branch develop \ --single-branch - restore_cache: diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index c36ba0571..4b4fc31a6 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -78,13 +78,6 @@ services: - ../src/:/var/src ports: - '3000:3000' - - '9229:9229' - # command: > - # sh -c "sleep 20 && - # npm run db:init && - # npm run db:seed:all && - # nodemon --trace-warnings app.js" - #command: ['npm','run','dev'] environment: - KAFKA_URL=kafka:9092 - USER_SERVICE_HOST=http://user:3001 @@ -112,12 +105,6 @@ services: - ../../user/src/:/var/src ports: - '3001:3001' - # - '9229:9229' - # command: > - # sh -c "sleep 20 && - # npm run db:init && - # npm run db:seed:all && - # nodemon --trace-warnings app.js" environment: - KAFKA_URL=kafka:9092 - REDIS_HOST=redis://redis:6379 diff --git a/src/configs/kafka.ci.js b/src/configs/kafka.ci.js deleted file mode 100644 index b0e59bc4d..000000000 --- a/src/configs/kafka.ci.js +++ /dev/null @@ -1,173 +0,0 @@ -/** - * name : configs/kafka - * author : Aman Gupta - * Date : 07-Dec-2021 - * Description : Kafka connection configurations - */ - -const utils = require('@generics/utils') -const { elevateLog } = require('elevate-logger') -const logger = elevateLog.init() -const { Kafka } = require('kafkajs') -const deleteuserConsumer = require('@generics/kafka/consumers/deleteuser') -const rolechangeConsumer = require('@generics/kafka/consumers/rolechange') -const createuserConsumer = require('@generics/kafka/consumers/createuser') -const updateuserConsumer = require('@generics/kafka/consumers/updateuser') -const organizationConsumer = require('@generics/kafka/consumers/organization') - -module.exports = async () => { - const kafkaIps = process.env.KAFKA_URL.split(',') - const KafkaClient = new Kafka({ - clientId: 'mentoring', - brokers: kafkaIps, - }) - - const producer = KafkaClient.producer() - await producer.connect() - - producer.on('producer.connect', () => { - logger.info('KafkaProvider: connected') - }) - producer.on('producer.disconnect', () => { - logger.error('KafkaProvider: could not connect', { - triggerNotification: true, - }) - }) - - global.kafkaProducer = producer - global.kafkaClient = KafkaClient - - startConsumer(KafkaClient).catch((err) => - logger.error('Kafka consumer failed to start', { err: err?.stack || err?.message }) - ) -} - -async function startConsumer(kafkaClient) { - const consumer = kafkaClient.consumer({ - groupId: process.env.KAFKA_GROUP_ID, - sessionTimeout: 45000, // allows slow handlers - heartbeatInterval: 3000, - }) - - // Lifecycle logs (important) - consumer.on(consumer.events.GROUP_JOIN, (e) => { - logger.info(`Kafka Consumer: Group join — partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) - }) - - consumer.on(consumer.events.REBALANCING, (e) => { - logger.warn(`Kafka Consumer: Rebalancing triggered — ${e.payload.reason}`) - }) - - consumer.on(consumer.events.HEARTBEAT, () => { - logger.debug('Kafka Consumer: Heartbeat OK') - }) - - await consumer.connect() - logger.info('Kafka Consumer: Connected to broker') - - const topics = [process.env.EVENTS_TOPIC, process.env.CLEAR_INTERNAL_CACHE] - - await consumer.subscribe({ topics }) - logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) - - //------------------------------------------------------------------ - // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) - //------------------------------------------------------------------ - await consumer.run({ - autoCommit: true, // safe because processing is fast per message - eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { - logger.info( - `Kafka Batch: Received batch | topic=${batch.topic} | partition=${batch.partition} | size=${batch.messages.length}` - ) - - for (const message of batch.messages) { - if (!isRunning() || isStale()) { - logger.warn('Kafka Batch: Consumer is no longer running or batch is stale, stopping processing.') - break - } - - const rawValue = message.value?.toString() - const offset = message.offset - const topic = batch.topic - const partition = batch.partition - - logger.info(`Kafka Batch: Message | topic=${topic} | partition=${partition} | offset=${offset}`) - - if (!rawValue) { - logger.warn(`Kafka Batch: Empty message skipped`) - resolveOffset(offset) - continue - } - - let payload - try { - payload = JSON.parse(rawValue) - } catch (e) { - logger.warn('Kafka Batch: Invalid JSON, skipping', { - offset, - err: e.message, - }) - resolveOffset(offset) - continue - } - - //-------------------------------------------------------- - // ROUTE MESSAGE TO CORRECT HANDLER - //-------------------------------------------------------- - let response - - try { - if (topic === process.env.EVENTS_TOPIC && payload) { - if (payload.eventType === 'roleChange') { - response = await rolechangeConsumer.messageReceived(payload) - } - - if (payload.eventType === 'create' || payload.eventType === 'bulk-create') { - response = await createuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'delete') { - response = await deleteuserConsumer.messageReceived(payload) - } - - if (payload.eventType === 'update' || payload.eventType === 'bulk-update') { - response = await updateuserConsumer.messageReceived(payload) - } - - // organization events - if ( - payload.entity === 'organization' && - (payload.eventType === 'create' || - payload.eventType === 'update' || - payload.eventType === 'deactivate') - ) { - response = await organizationConsumer.messageReceived(payload) - } - } else if (topic === process.env.CLEAR_INTERNAL_CACHE && payload?.type === 'CLEAR_INTERNAL_CACHE') { - response = await utils.internalDel(payload.value) - } - - logger.info(`Kafka Batch: Handler response for offset=${offset} => ${JSON.stringify(response)}`) - } catch (handlerErr) { - logger.error(`Kafka Batch: Error handling message`, { - offset, - err: handlerErr.stack || handlerErr.message, - }) - } - - //-------------------------------------------------------- - // MARK MESSAGE AS PROCESSED - //-------------------------------------------------------- - resolveOffset(offset) - - //-------------------------------------------------------- - // HEARTBEAT TO PREVENT TIMEOUT - //-------------------------------------------------------- - await heartbeat() - } - - // commit offsets once per batch - await commitOffsetsIfNecessary() - }, - }) -} diff --git a/src/configs/kafka.js b/src/configs/kafka.js index f7bdbebde..d7c9c6d27 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -49,7 +49,6 @@ async function startConsumer(kafkaClient) { heartbeatInterval: 3000, }) - // Lifecycle logs (important) consumer.on(consumer.events.GROUP_JOIN, (e) => { logger.info(`Kafka Consumer: Group join – partitions assigned = ${JSON.stringify(e.payload?.memberAssignment)}`) }) @@ -70,9 +69,6 @@ async function startConsumer(kafkaClient) { await consumer.subscribe({ topics }) logger.info(`Kafka Consumer: Subscribed to topics = ${JSON.stringify(topics)}`) - //------------------------------------------------------------------ - // EACH BATCH VERSION (FIXES HEARTBEAT + MISSED MESSAGES) - //------------------------------------------------------------------ await consumer.run({ autoCommit: true, // safe because processing is fast per message eachBatch: async ({ batch, heartbeat, resolveOffset, commitOffsetsIfNecessary, isRunning, isStale }) => { @@ -161,7 +157,7 @@ async function startConsumer(kafkaClient) { } //-------------------------------------------------------- - // MARK MESSAGE AS PROCESSED + // MARKS MESSAGE AS PROCESSED //-------------------------------------------------------- resolveOffset(offset) From 3f171a39ff663ec87741f1295aae7d5f0af41ef8 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 15:25:05 +0530 Subject: [PATCH 62/66] savepoint-3 minor fixes --- .circleci/config.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b5d91838..b76de402e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -94,12 +94,6 @@ jobs: name: Copy config.json command: cp dev-ops/integration_test.config.json src/config.json - - run: - name: Prepare Kafka config for CI - command: | - cp src/configs/kafka.ci.js src/configs/kafka.js - - # ------------------------------- # Start docker-compose # ------------------------------- From 41ca559a58c7b42e5a949fe3124b0cb246f7f491 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 16:02:37 +0530 Subject: [PATCH 63/66] deleted config.json --- src/config.json | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/config.json diff --git a/src/config.json b/src/config.json deleted file mode 100644 index b8468f1e0..000000000 --- a/src/config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "authTokenUserInformation": { - "id": "data.id", - "name": "data.name", - "organization_code": "data.organizations[?id={{organization_id}}].code", - "organization_id": "data.organization_ids[0]", - "organizations": "data.organizations", - "roles": "data.organizations[?id={{organization_id}}].roles", - "tenant_code": "data.tenant_code" - } -} From ce89008e4737aec00d6c77c81137636058b446a2 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 16:07:46 +0530 Subject: [PATCH 64/66] undoed migration changes from this PR --- .../20230609052656-create-user-extension.js | 8 --- ...612030911-create-organisation-extension.js | 8 --- ...session-columns-and-update-mentor-names.js | 5 +- ...ltering-session-ownership-tabe-and-data.js | 57 ------------------- ...20240716111210-update-user-id-to-string.js | 8 +-- .../20251020081719-add-orgEntity-type.js | 4 +- src/database/queries/sessions.js | 29 ---------- ...822124704-add_entity_types_and_entities.js | 3 - ...4041327-add_questions_and_question_sets.js | 4 -- .../seeders/20231103090632-seed-forms.js | 6 -- ...20231115170949-add-new-mentee-questions.js | 2 - 11 files changed, 7 insertions(+), 127 deletions(-) delete mode 100644 src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js diff --git a/src/database/migrations/20230609052656-create-user-extension.js b/src/database/migrations/20230609052656-create-user-extension.js index e12f48fb1..6b17c1378 100644 --- a/src/database/migrations/20230609052656-create-user-extension.js +++ b/src/database/migrations/20230609052656-create-user-extension.js @@ -47,14 +47,6 @@ module.exports = { custom_entity_text: { type: Sequelize.JSON, }, - organization_code: { - type: Sequelize.STRING, - allowNull: false, - }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20230612030911-create-organisation-extension.js b/src/database/migrations/20230612030911-create-organisation-extension.js index 61c45fc07..e4f6fd6ed 100644 --- a/src/database/migrations/20230612030911-create-organisation-extension.js +++ b/src/database/migrations/20230612030911-create-organisation-extension.js @@ -27,14 +27,6 @@ module.exports = { allow_mentor_override: { type: Sequelize.BOOLEAN, }, - organization_code: { - type: Sequelize.STRING, - allowNull: false, - }, - tenant_code: { - type: Sequelize.STRING, - allowNull: false, - }, created_at: { allowNull: false, type: Sequelize.DATE, diff --git a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js index 5ea838acd..d451d8f41 100644 --- a/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js +++ b/src/database/migrations/20231228184838-add-session-columns-and-update-mentor-names.js @@ -33,10 +33,7 @@ module.exports = { // Logic to update mentor names const updateMentorNamesInSessions = async () => { try { - const sessionsWithNullMentorName = await queryInterface.sequelize.query( - "SELECT * FROM sessions WHERE mentor_name = 'Mentor'", - { type: Sequelize.QueryTypes.SELECT } - ) + const sessionsWithNullMentorName = await sessionQueries.findAll({ mentor_name: 'Mentor' }) if (sessionsWithNullMentorName.length === 0) { console.log('No sessions found with mentor_name as null.') diff --git a/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js b/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js deleted file mode 100644 index 530fe5b78..000000000 --- a/src/database/migrations/20240213074857-altering-session-ownership-tabe-and-data.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' - -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - // Remove the primary key constraint on mentor_id - await queryInterface.removeConstraint('session_ownerships', 'session_ownerships_pkey') - // change mentor_id column name - await queryInterface.renameColumn('session_ownerships', 'mentor_id', 'user_id') - // add new column called type - await queryInterface.addColumn('session_ownerships', 'type', { - allowNull: false, - type: Sequelize.STRING, - defaultValue: 'MENTOR', - primaryKey: true, - }) - - // create new entries for creator type for old data - const oldSessionOwnerships = await queryInterface.sequelize.query('SELECT * FROM session_ownerships', { - type: Sequelize.QueryTypes.SELECT, - }) - // Do only if old data is present - if (oldSessionOwnerships.length > 0) { - const creatorEntries = oldSessionOwnerships.map((entry) => ({ - ...entry, - type: 'CREATOR', - })) - - await queryInterface.bulkInsert('session_ownerships', creatorEntries) - } - - await queryInterface.addConstraint('session_ownerships', { - fields: ['user_id', 'session_id', 'type'], - type: 'primary key', - name: 'session_ownerships_user_session_type_pkey', - }) - }, - - async down(queryInterface, Sequelize) { - // Delete all entries where type='CREATOR' - await queryInterface.bulkDelete('session_ownerships', { type: 'CREATOR' }) - - // remove added primarykey constrain - await queryInterface.removeConstraint('session_ownerships', 'session_ownerships_user_session_type_pkey') - // revert column change - await queryInterface.renameColumn('session_ownerships', 'user_id', 'mentor_id') - // remove type column - await queryInterface.removeColumn('session_ownerships', 'type') - - // Add back the primary key constraint on mentor_id - await queryInterface.addConstraint('session_ownerships', { - fields: ['mentor_id', 'session_id'], - type: 'primary key', - name: 'session_ownerships_pkey', - }) - }, -} diff --git a/src/database/migrations/20240716111210-update-user-id-to-string.js b/src/database/migrations/20240716111210-update-user-id-to-string.js index 8baafbae6..8aaa64c97 100644 --- a/src/database/migrations/20240716111210-update-user-id-to-string.js +++ b/src/database/migrations/20240716111210-update-user-id-to-string.js @@ -20,10 +20,10 @@ module.exports = { type: Sequelize.STRING, allowNull: false, }) - // await queryInterface.changeColumn('session_ownerships', 'user_id', { - // type: Sequelize.STRING, - // allowNull: false, - // }) + await queryInterface.changeColumn('session_ownerships', 'user_id', { + type: Sequelize.STRING, + allowNull: false, + }) await queryInterface.changeColumn('user_extensions', 'user_id', { type: Sequelize.STRING, allowNull: false, diff --git a/src/database/migrations/20251020081719-add-orgEntity-type.js b/src/database/migrations/20251020081719-add-orgEntity-type.js index 5855c95ca..c095015b1 100644 --- a/src/database/migrations/20251020081719-add-orgEntity-type.js +++ b/src/database/migrations/20251020081719-add-orgEntity-type.js @@ -28,8 +28,8 @@ module.exports = { updated_by: 0, allow_filtering: false, organization_id: defaultOrgId, - //organization_code: defaultOrgCode, - //tenant_code: defaultTenantCode, + organization_code: defaultOrgCode, + tenant_code: defaultTenantCode, has_entities: false, meta: JSON.stringify({ label: convertToWords(key), diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index d9f3b8a24..a648019e1 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -160,35 +160,6 @@ exports.updateRecords = async (data, options = {}) => { } } -exports.findAllMigrations = async (filter) => { - try { - const whereClauses = [] - const replacements = {} - - Object.entries(filter).forEach(([key, value]) => { - if (value === undefined) return - - whereClauses.push(`"${key}" = :${key}`) - replacements[key] = value - }) - - const sql = ` - SELECT * - FROM sessions - ${whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : ''} - ` - - const result = await Sequelize.query(sql, { - replacements, - type: QueryTypes.SELECT, - }) - - return result - } catch (error) { - return error - } -} - exports.findAll = async (filter, tenantCode, options = {}) => { try { filter.tenant_code = tenantCode diff --git a/src/database/seeders/20230822124704-add_entity_types_and_entities.js b/src/database/seeders/20230822124704-add_entity_types_and_entities.js index e61fd703c..5ed8fcadc 100644 --- a/src/database/seeders/20230822124704-add_entity_types_and_entities.js +++ b/src/database/seeders/20230822124704-add_entity_types_and_entities.js @@ -120,8 +120,6 @@ module.exports = { allow_filtering: true, organization_id: defaultOrgId, has_entities: true, - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, } // Check if the key is in sessionEntityTypes before adding model_names @@ -155,7 +153,6 @@ module.exports = { eachEntity.created_at = new Date() eachEntity.updated_at = new Date() eachEntity.created_by = 0 - eachEntity.tenant_code = process.env.DEFAULT_TENANT_CODE entitiesFinalArray.push(eachEntity) }) diff --git a/src/database/seeders/20230824041327-add_questions_and_question_sets.js b/src/database/seeders/20230824041327-add_questions_and_question_sets.js index ac1d9c45a..cd6aea0e5 100644 --- a/src/database/seeders/20230824041327-add_questions_and_question_sets.js +++ b/src/database/seeders/20230824041327-add_questions_and_question_sets.js @@ -104,8 +104,6 @@ module.exports = { status: 'PUBLISHED', updated_at: new Date(), created_at: new Date(), - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, } questionSetFinalArray.push(questionSetRow) @@ -118,8 +116,6 @@ module.exports = { questionsArray[questionSet].forEach((question) => { question.created_at = new Date() question.updated_at = new Date() - question.organization_code = process.env.DEFAULT_ORGANIZATION_CODE - question.tenant_code = process.env.DEFAULT_TENANT_CODE question.rendering_data = JSON.stringify(question.rendering_data) if (question.category) { diff --git a/src/database/seeders/20231103090632-seed-forms.js b/src/database/seeders/20231103090632-seed-forms.js index 266b38699..226453e9a 100644 --- a/src/database/seeders/20231103090632-seed-forms.js +++ b/src/database/seeders/20231103090632-seed-forms.js @@ -920,12 +920,6 @@ module.exports = { created_at: new Date(), }, ] - - formData.forEach((form) => { - form.organization_code = process.env.DEFAULT_ORGANIZATION_CODE - form.tenant_code = process.env.DEFAULT_TENANT_CODE - }) - await queryInterface.bulkInsert('forms', formData, {}) } catch (error) { console.error('Error seeding forms:', error) diff --git a/src/database/seeders/20231115170949-add-new-mentee-questions.js b/src/database/seeders/20231115170949-add-new-mentee-questions.js index 09cb2568c..24186a23b 100644 --- a/src/database/seeders/20231115170949-add-new-mentee-questions.js +++ b/src/database/seeders/20231115170949-add-new-mentee-questions.js @@ -54,8 +54,6 @@ module.exports = { }, updated_at: new Date(), created_at: new Date(), - organization_code: process.env.DEFAULT_ORGANIZATION_CODE, - tenant_code: process.env.DEFAULT_TENANT_CODE, } questionsFinalArray = questionsArray.map((question) => ({ ...question, ...additionalObject })) From 79e17ae99575dc7dda4bf4b0d25027c0c092b390 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 16:28:11 +0530 Subject: [PATCH 65/66] updated package json file --- src/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/package.json b/src/package.json index 1f7639eff..d15eb140e 100644 --- a/src/package.json +++ b/src/package.json @@ -16,9 +16,7 @@ "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", "db:init": "(sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check') && sequelize-cli db:migrate", - "db:init:integration": "sequelize-cli db:create || echo 'exists'; node scripts/run-migrations.js", - "db:seed:all": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' ", - "db:seed:all:integration": "node scripts/run-seeders.js" + "db:seed:all": "sequelize-cli db:seed:all || echo 'Seeded data already exists or some issue while seeding the data, Please check' " }, "author": "Aman Kumar Gupta ", "license": "ISC", From b8803888f380bbd133e061697b50f653261b6d6a Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 20 Jan 2026 16:31:57 +0530 Subject: [PATCH 66/66] package json updated --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index d15eb140e..0daa0e1ab 100644 --- a/src/package.json +++ b/src/package.json @@ -11,7 +11,7 @@ "start": "NODE_ENV=development nodemon app.js", "prod": "NODE_ENV=production node app.js", "stage": "NODE_ENV=stage node app.js", - "dev": "node --inspect=0.0.0.0:9229 app.js", + "dev": "node app.js", "qa": "NODE_ENV=qa node app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand",