From 2ecf3c6eff2ab52d6710a58fc2f7b076bffcbda8 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:50:26 -0800 Subject: [PATCH] GitOps: added support to Switcher Relay - new/changed events --- npm-shrinkwrap.json | 61 +++-- package.json | 4 +- requests/Switcher API.postman_collection.json | 76 ++++++ src/client/configuration-resolvers.js | 5 +- src/services/config.js | 11 +- src/services/gitops/push-changed.js | 22 +- src/services/gitops/push-new.js | 32 ++- tests/fixtures/db_client.js | 2 +- tests/gitops.test.js | 222 +++++++++++++++++- 9 files changed, 390 insertions(+), 45 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d2498b2..bd3a099 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -17,13 +17,13 @@ "express-rate-limit": "^7.4.1", "express-validator": "^7.2.0", "graphql": "^16.9.0", - "graphql-http": "^1.22.1", + "graphql-http": "^1.22.2", "graphql-tag": "^2.12.6", "helmet": "^8.0.0", "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", "mongodb": "^6.10.0", - "mongoose": "^8.8.0", + "mongoose": "^8.8.1", "pino": "^9.5.0", "pino-pretty": "^11.3.0", "swagger-ui-express": "^5.0.1", @@ -679,9 +679,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", - "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "engines": { "node": ">=18.18" @@ -1133,6 +1133,12 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1270,9 +1276,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", - "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { "undici-types": "~6.19.8" @@ -1854,9 +1860,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001677", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", - "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "version": "1.0.30001679", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", + "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", "dev": true, "funding": [ { @@ -2124,9 +2130,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -2282,9 +2288,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "version": "1.5.55", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", + "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", "dev": true }, "node_modules/emittery": { @@ -3099,9 +3105,9 @@ } }, "node_modules/graphql-http": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/graphql-http/-/graphql-http-1.22.1.tgz", - "integrity": "sha512-4Jor+LRbA7SfSaw7dfDUs2UBzvWg3cKrykfHRgKsOIvQaLuf+QOcG2t3Mx5N9GzSNJcuqMqJWz0ta5+BryEmXg==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/graphql-http/-/graphql-http-1.22.2.tgz", + "integrity": "sha512-OpUJJoefHlyfYoOyhzIVjKxFPIDylmX34wy2y8M/i9i+DcQTGYmuThLGenuS92tFRzJnAXO2HJYQoz8O9TLcEg==", "engines": { "node": ">=12" }, @@ -4606,9 +4612,9 @@ } }, "node_modules/mongoose": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.0.tgz", - "integrity": "sha512-KluvgwnQB1GPOYZZXUHJRjS1TW6xxwTlf/YgjWExuuNanIe3W7VcR7dDXQVCIRk8L7NYge8EnoTcu2grWtN+XQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.1.tgz", + "integrity": "sha512-l7DgeY1szT98+EKU8GYnga5WnyatAu+kOQ2VlVX1Mxif6A0Umt0YkSiksCiyGxzx8SPhGe9a53ND1GD4yVDrPA==", "dependencies": { "bson": "^6.7.0", "kareem": "2.6.3", @@ -5906,9 +5912,12 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.17.14", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", - "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz", + "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } }, "node_modules/swagger-ui-express": { "version": "5.0.1", diff --git a/package.json b/package.json index 27c7be5..8ec88a1 100644 --- a/package.json +++ b/package.json @@ -44,13 +44,13 @@ "express-rate-limit": "^7.4.1", "express-validator": "^7.2.0", "graphql": "^16.9.0", - "graphql-http": "^1.22.1", + "graphql-http": "^1.22.2", "graphql-tag": "^2.12.6", "helmet": "^8.0.0", "jsonwebtoken": "^9.0.2", "moment": "^2.30.1", "mongodb": "^6.10.0", - "mongoose": "^8.8.0", + "mongoose": "^8.8.1", "pino": "^9.5.0", "pino-pretty": "^11.3.0", "swagger-ui-express": "^5.0.1", diff --git a/requests/Switcher API.postman_collection.json b/requests/Switcher API.postman_collection.json index 7674702..fd4549f 100644 --- a/requests/Switcher API.postman_collection.json +++ b/requests/Switcher API.postman_collection.json @@ -5254,6 +5254,44 @@ }, "response": [] }, + { + "name": "GitOps Push - Change Relay", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{gitopsToken}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"environment\": \"default\",\r\n \"changes\": [\r\n {\r\n \"action\": \"CHANGED\",\r\n \"diff\": \"CONFIG\",\r\n \"path\": [\r\n \"Group Test\",\r\n \"NEW_SWITCHER_2\"\r\n ],\r\n \"content\": {\r\n \"relay\": {\r\n \"type\": \"NOTIFICATION\",\r\n \"method\": \"POST\",\r\n \"endpoint\": \"https://localhost:3000\",\r\n \"description\": \"Push message to log\",\r\n \"activated\": true\r\n }\r\n }\r\n }\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/gitops/v1/push", + "host": [ + "{{url}}" + ], + "path": [ + "gitops", + "v1", + "push" + ] + } + }, + "response": [] + }, { "name": "GitOps Push - Change Strategy", "request": { @@ -5406,6 +5444,44 @@ }, "response": [] }, + { + "name": "GitOps Push - New Switcher / Relay", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{gitopsToken}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"environment\": \"default\",\r\n \"changes\": [\r\n {\r\n \"action\": \"NEW\",\r\n \"diff\": \"CONFIG\",\r\n \"path\": [\r\n \"New Group\"\r\n ],\r\n \"content\": {\r\n \"key\": \"NEW_SWITCHER_RELAY\",\r\n \"description\": \"New Switcher Relay\",\r\n \"activated\": true,\r\n \"components\": [\r\n \"switcher-playground\"\r\n ],\r\n \"relay\": {\r\n \"type\": \"NOTIFICATION\",\r\n \"method\": \"POST\",\r\n \"endpoint\": \"https://localhost:3000\",\r\n \"description\": \"Push message to log\",\r\n \"activated\": true\r\n }\r\n }\r\n }\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/gitops/v1/push", + "host": [ + "{{url}}" + ], + "path": [ + "gitops", + "v1", + "push" + ] + } + }, + "response": [] + }, { "name": "GitOps Push - New Switcher / Strategy", "request": { diff --git a/src/client/configuration-resolvers.js b/src/client/configuration-resolvers.js index f40735b..e3b0a7a 100644 --- a/src/client/configuration-resolvers.js +++ b/src/client/configuration-resolvers.js @@ -97,7 +97,10 @@ export async function resolveFlatConfig(source, context) { configs = source.config; domainId = configs[0].domain; } else if (source.group) { - configs = await getConfigs({ group: source.group.map(g => g._id) }, true); + configs = await getConfigs({ + domain: source.group[0].domain, + group: source.group.map(g => g._id) + }, true); domainId = source.group[0].domain; } diff --git a/src/services/config.js b/src/services/config.js index a15424c..137aee7 100644 --- a/src/services/config.js +++ b/src/services/config.js @@ -29,8 +29,9 @@ export async function getConfigById(id, populateAdmin = false) { export async function getConfig(where) { const query = Config.findOne(); - if (where.domain) query.where('domain', where.domain); - if (where.key) query.where('key', where.key); + query.where('key', where.key); + query.where('domain', where.domain); + if (where.group) query.where('group', where.group); return query.exec(); @@ -39,9 +40,10 @@ export async function getConfig(where) { export async function getConfigs(where, lean = false) { const query = Config.find(); + query.where('domain', where.domain); + if (where.id) query.where('_id', where.id); if (where.key) query.where('key', where.key); - if (where.domain) query.where('domain', where.domain); if (where.group) query.where('group', where.group); if (lean) query.lean(); @@ -70,6 +72,9 @@ export async function createConfig(args, admin) { key: args.key, domain: group.domain }); + + // validates relay + isRelayValid(args.relay); if (config) { throw new BadRequestError(`Config ${config.key} already exists`); diff --git a/src/services/gitops/push-changed.js b/src/services/gitops/push-changed.js index 360ccc5..7ece52f 100644 --- a/src/services/gitops/push-changed.js +++ b/src/services/gitops/push-changed.js @@ -1,5 +1,5 @@ import { getStrategies, updateStrategy } from '../config-strategy.js'; -import { getConfig, updateConfig } from '../config.js'; +import { getConfig, updateConfig, updateConfigRelay } from '../config.js'; import { getGroupConfig, updateGroup } from '../group-config.js'; import { ADMIN_EMAIL } from './index.js'; @@ -27,12 +27,16 @@ async function processChangedGroup(domain, change, environment) { async function processChangedConfig(domain, change, environment) { const content = change.content; const admin = { _id: domain.owner, email: ADMIN_EMAIL }; - const config = await getConfig({ domain: domain._id, key: content.key }); + const config = await getConfig({ domain: domain._id, key: change.path[1] }); await updateConfig(config._id, { description: getChangedValue(content.description, config.description), activated: new Map().set(environment, getChangedValue(content.activated, config.activated.get(environment))) }, admin); + + if (content.relay) { + await updateConfigRelay(config._id, processRelay(content.relay, config.relay, environment), admin); + } } async function processChangedStrategy(domain, change, environment) { @@ -52,6 +56,20 @@ async function processChangedStrategy(domain, change, environment) { }, admin); } +function processRelay(content, configRelay, environment) { + return { + type: getChangedValue(content.type, configRelay.type), + method: getChangedValue(content.method, configRelay.method), + description: getChangedValue(content.description, configRelay.description), + activated: { + [environment]: getChangedValue(content.activated, configRelay.activated?.get(environment)) + }, + endpoint: { + [environment]: getChangedValue(content.endpoint, configRelay.endpoint?.get(environment)) + } + }; +} + function getChangedValue(changeValue, defaultValue) { return changeValue !== undefined ? changeValue : defaultValue; } \ No newline at end of file diff --git a/src/services/gitops/push-new.js b/src/services/gitops/push-new.js index d585f62..b35559a 100644 --- a/src/services/gitops/push-new.js +++ b/src/services/gitops/push-new.js @@ -42,21 +42,35 @@ async function processNewConfig(domain, change, environment) { const admin = { _id: domain.owner }; const group = await getGroupConfig({ domain: domain._id, name: path[0] }); - let componentIds; - if (content.components?.length) { - const components = await getComponents({ domain: domain._id, name: { $in: content.components } }); - componentIds = components.map(component => component._id); - } - - const config = await createConfig({ + const newConfig = { domain: domain._id, group: group._id, key: content.key, description: getNewValue(content.description, ''), activated: new Map().set(environment, getNewValue(content.activated, true)), owner: domain.owner, - components: componentIds - }, admin); + }; + + if (content.components?.length) { + const components = await getComponents({ domain: domain._id, name: { $in: content.components } }); + newConfig.components = components.map(component => component._id); + } + + if (content.relay) { + newConfig.relay = { + type: content.relay.type, + method: content.relay.method, + endpoint: { + [environment]: content.relay.endpoint + }, + description: content.relay.description, + activated: { + [environment]: getNewValue(content.relay.activated, true) + } + }; + } + + const config = await createConfig(newConfig, admin); if (content.strategies?.length) { for (const strategy of content.strategies) { diff --git a/tests/fixtures/db_client.js b/tests/fixtures/db_client.js index 013993d..77656e4 100644 --- a/tests/fixtures/db_client.js +++ b/tests/fixtures/db_client.js @@ -82,7 +82,7 @@ export const configPrdQADocument = { key: keyConfigPrdQA, description: 'Test config 2 - Off in PRD and ON in QA', activated: { - [`${EnvType.DEFAULT}`]: false, + [EnvType.DEFAULT]: false, QA: true }, owner: adminMasterAccountId, diff --git a/tests/gitops.test.js b/tests/gitops.test.js index 9a595ff..9b6bb99 100644 --- a/tests/gitops.test.js +++ b/tests/gitops.test.js @@ -3,7 +3,7 @@ import request from 'supertest'; import jwt from 'jsonwebtoken'; import app from '../src/app'; import { Client } from 'switcher-client'; -import { Config } from '../src/models/config'; +import { Config, RelayMethods, RelayTypes } from '../src/models/config'; import GroupConfig from '../src/models/group-config'; import { ConfigStrategy, OperationsType, StrategiesType } from '../src/models/config-strategy'; import { EnvType } from '../src/models/environment'; @@ -189,6 +189,59 @@ describe('GitOps - Push New', () => { expect(config.components).toHaveLength(0); }); + test('GITOPS_SUITE - Should push changes - New Switcher and Relay', async () => { + const token = generateToken('30s'); + + const lastUpdate = Date.now(); + const req = await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: EnvType.DEFAULT, + changes: [{ + action: 'NEW', + diff: 'CONFIG', + path: [ + 'Group Test' + ], + content: { + key: 'NEW_SWITCHER_RELAY', + activated: true, + relay: { + type: RelayTypes.NOTIFICATION, + method: RelayMethods.POST, + description: 'New Relay', + activated: true, + endpoint: 'https://localhost:3000' + } + } + }] + }) + .expect(200); + + expect(req.body.message).toBe('Changes applied successfully'); + expect(req.body.version).toBeGreaterThan(lastUpdate); + + // Check if the changes were applied + const config = await Config.findOne({ key: 'NEW_SWITCHER_RELAY', domain: domainId }).lean().exec(); + expect(config).not.toBeNull(); + expect(config.activated[EnvType.DEFAULT]).toBe(true); + expect(config.relay).toMatchObject({ + type: RelayTypes.NOTIFICATION, + method: RelayMethods.POST, + description: 'New Relay', + endpoint: { + [EnvType.DEFAULT]: 'https://localhost:3000' + }, + activated: { + [EnvType.DEFAULT]: true + }, + verified: { + [EnvType.DEFAULT]: false + } + }); + }); + test('GITOPS_SUITE - Should push changes - New Switcher and Strategy', async () => { const token = generateToken('30s'); @@ -407,6 +460,98 @@ describe('GitOps - Push Changed', () => { expect(config.description).toBe('Changed Switcher Description'); }); + test('GITOPS_SUITE - Should push changes - Changed Switcher Relay (added)', async () => { + const token = generateToken('30s'); + + const lastUpdate = Date.now(); + const req = await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: EnvType.DEFAULT, + changes: [{ + action: 'CHANGED', + diff: 'CONFIG', + path: ['Group Test', 'TEST_CONFIG_KEY_PRD_QA'], + content: { + relay: { + type: RelayTypes.NOTIFICATION, + method: RelayMethods.POST, + description: 'New Relay', + activated: true, + endpoint: 'https://localhost:3000' + } + } + }] + }) + .expect(200); + + expect(req.body.message).toBe('Changes applied successfully'); + expect(req.body.version).toBeGreaterThan(lastUpdate); + + // Check if the changes were applied + const config = await Config.findOne({ key: 'TEST_CONFIG_KEY_PRD_QA', domain: domainId }).lean().exec(); + expect(config).not.toBeNull(); + expect(config.relay).toMatchObject({ + type: RelayTypes.NOTIFICATION, + method: RelayMethods.POST, + description: 'New Relay', + activated: { + [EnvType.DEFAULT]: true + }, + endpoint: { + [EnvType.DEFAULT]: 'https://localhost:3000' + }, + verified: { + [EnvType.DEFAULT]: false + } + }); + }); + + test('GITOPS_SUITE - Should push changes - Changed Switcher Relay environment status/endpoint', async () => { + const token = generateToken('30s'); + + const lastUpdate = Date.now(); + const req = await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: 'QA', + changes: [{ + action: 'CHANGED', + diff: 'CONFIG', + path: ['Group Test', 'TEST_CONFIG_KEY'], + content: { + relay: { + endpoint: 'http://localhost:3001', + activated: false + } + } + }] + }) + .expect(200); + + expect(req.body.message).toBe('Changes applied successfully'); + expect(req.body.version).toBeGreaterThan(lastUpdate); + + // Check if the changes were applied + const config = await Config.findOne({ key: 'TEST_CONFIG_KEY', domain: domainId }).lean().exec(); + expect(config).not.toBeNull(); + expect(config.relay).toMatchObject({ + type: RelayTypes.NOTIFICATION, + method: RelayMethods.POST, + description: 'Test Relay', + activated: { + ['QA']: false, + [EnvType.DEFAULT]: true + }, + endpoint: { + ['QA']: 'http://localhost:3001', + [EnvType.DEFAULT]: 'http://localhost:3000' + } + }); + }); + test('GITOPS_SUITE - Should push changes - Changed Strategy', async () => { const token = generateToken('30s'); @@ -446,6 +591,81 @@ describe('GitOps - Push Changed', () => { }, }); }); + + test('GITOPS_SUITE - Should push changes - Changed Strategy environment status', async () => { + const token = generateToken('30s'); + + // given + const changes = [{ + action: 'NEW', + diff: 'STRATEGY', + path: ['Group Test', 'TEST_CONFIG_KEY'], + content: { + strategy: StrategiesType.NUMERIC, + description: 'Test Strategy', + operation: OperationsType.EXIST, + activated: true, + values: ['100', '200'] + } + }]; + + // default environment + await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: EnvType.DEFAULT, + changes + }); + + // QA environment + await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: 'QA', + changes + }); + + // test + const lastUpdate = Date.now(); + const req = await request(app) + .post('/gitops/v1/push') + .set('Authorization', `Bearer ${token}`) + .send({ + environment: 'QA', + changes: [{ + action: 'CHANGED', + diff: 'STRATEGY', + path: ['Group Test', 'TEST_CONFIG_KEY', StrategiesType.NUMERIC], + content: { + activated: false + } + }] + }) + .expect(200); + + expect(req.body.message).toBe('Changes applied successfully'); + expect(req.body.version).toBeGreaterThan(lastUpdate); + + // Check if the changes were applied + const strategies = await ConfigStrategy.find({ + config: configId, + strategy: StrategiesType.NUMERIC + }).lean().exec(); + + expect(strategies).toHaveLength(2); + expect(strategies[0]).toMatchObject({ + activated: { + [EnvType.DEFAULT]: true + } + }); + expect(strategies[1]).toMatchObject({ + activated: { + ['QA']: false + } + }); + }); });