diff --git a/src/from-config.test.ts b/src/from-config.test.ts index 68ae4de..fce1bf2 100644 --- a/src/from-config.test.ts +++ b/src/from-config.test.ts @@ -1,118 +1,122 @@ -import specs from '@unleash/client-specification/specifications/index.json' -import got from 'got' -import YAML from 'yaml' -import fs from 'fs' -import { GenericContainer, Network, StartedNetwork } from 'testcontainers' +import specs from "@unleash/client-specification/specifications/index.json"; +import got from "got"; +import YAML from "yaml"; +import fs from "fs"; +import { GenericContainer, Network, StartedNetwork } from "testcontainers"; import { MockServerConfig, OSSServerConfig, parseResult, TestConfiguration, - TestYamlConfig -} from './lib/Config' -import { ContainerInstance, UnleashServerInterface } from './lib/BaseContainers' -import { SDKOptions } from './lib/SDKContainers' + TestYamlConfig, +} from "./lib/Config"; +import { + ContainerInstance, + UnleashServerInterface, +} from "./lib/BaseContainers"; +import { SDKOptions } from "./lib/SDKContainers"; const path = require("path"); const rawConfig = fs.readFileSync( - `./src/${process.env.CONFIG || 'all-against-mock'}.yaml`, - 'utf8' -) -const parsedConfig = YAML.parse(rawConfig) as TestYamlConfig + `./src/${process.env.CONFIG || "all-against-mock"}.yaml`, + "utf8" +); +const parsedConfig = YAML.parse(rawConfig) as TestYamlConfig; const sdks = process.env.SDK ? [{ name: process.env.SDK, type: process.env.SDK }] - : parsedConfig.sdks.map(c => { - c.name = c.name || c.type - return c - }) + : parsedConfig.sdks.map((c) => { + c.name = c.name || c.type; + return c; + }); const servers = process.env.SERVER ? [{ type: process.env.SERVER }] - : parsedConfig.servers + : parsedConfig.servers; -const testDetails = parsedConfig.tests.load +const testDetails = parsedConfig.tests.load; -const excludeTests = testDetails.excluding || [] +const excludeTests = testDetails.excluding || []; function parseConfig( server: OSSServerConfig | MockServerConfig ): TestConfiguration { - let config: TestConfiguration - if (server.type === 'OSSServer') { - const ossServer = server as OSSServerConfig + let config: TestConfiguration; + if (server.type === "OSSServer") { + const ossServer = server as OSSServerConfig; config = { serverImpl: ossServer.type, postgres: { image: ossServer.postgresImage, - dbName: 'unleash', - user: 'unleash_user', - password: 'unleash.the.password' + dbName: "unleash", + user: "unleash_user", + password: "unleash.the.password", }, unleash: { image: ossServer.image, clientToken: parsedConfig.clientToken, - adminToken: parsedConfig.adminToken - } - } - } else if (server.type === 'MockServer' || server.type === 'Edge') { + adminToken: parsedConfig.adminToken, + }, + }; + } else if (server.type === "MockServer" || server.type === "Edge") { // This is pointless, we should remove TestConfiguration config = { serverImpl: server.type, postgres: { - image: '', - dbName: '', - user: '', - password: '' + image: "", + dbName: "", + user: "", + password: "", }, unleash: { - image: '', + image: "", clientToken: parsedConfig.clientToken, - adminToken: parsedConfig.adminToken - } - } + adminToken: parsedConfig.adminToken, + }, + }; } else { - throw new Error(`Invalid server config type ${server.type}`) + throw new Error(`Invalid server config type ${server.type}`); } - return config + return config; } function partition(arr: T[], check: (o: T) => boolean): T[][] { - let initialValue: T[][] = [[], []] + let initialValue: T[][] = [[], []]; return arr.reduce((result, element) => { - result[check(element) ? 0 : 1].push(element) - return result - }, initialValue) + result[check(element) ? 0 : 1].push(element); + return result; + }, initialValue); } const tests: string[] = specs.filter( - spec => !excludeTests.includes(spec.slice(0, 2)) -) - -describe.each(servers)(`$type`, server => { - let unleashServer: ContainerInstance & UnleashServerInterface - let network: StartedNetwork - let initialized = false - let sdkContainers = new Map() - let config = parseConfig(server) - describe.each(tests)(`%s suite`, testName => { + (spec) => !excludeTests.includes(spec.slice(0, 2)) +); + +describe.each(servers)(`$type`, (server) => { + let unleashServer: ContainerInstance & UnleashServerInterface; + let network: StartedNetwork; + let initialized = false; + let sdkContainers = new Map(); + let config = parseConfig(server); + describe.each(tests)(`%s suite`, (testName) => { // eslint-disable-next-line - const definition: ISpecDefinition = require(`@unleash/client-specification/specifications/${testName}`) + const definition: ISpecDefinition = require(`@unleash/client-specification/specifications/${testName}`); beforeAll(async () => { if (!initialized) { console.log( `===== Initializing Unleash ${server.type} ${definition.name} =====` - ) - network = await new Network().start() - + ); + network = await new Network().start(); + if (process.env.DEBUG) { - const ngrepContainer = await GenericContainer - .fromDockerfile(path.resolve('./src/tools/ngrep'), 'Dockerfile') - .build() + const ngrepContainer = await GenericContainer.fromDockerfile( + path.resolve("./src/tools/ngrep"), + "Dockerfile" + ).build(); await ngrepContainer .withNetworkMode("host") // bind to host network cause it will listen to docker bridged network - .withBindMount(path.resolve('.'), '/output') + .withBindMount(path.resolve("."), "/output") .withCmd([ "-q", // Be quiet; don't output any information other than packet headers and their payloads (if relevant). "-t", // Print a timestamp in the form of YYYY/MM/DD HH:MM:SS.UUUUUU everytime a packet is matched. @@ -120,85 +124,96 @@ describe.each(servers)(`$type`, server => { "-Wbyline", // Specify alternate manner for displaying packets `-dbr-${network.getId().slice(0, 12)}`, // listen to the bridge interface created for this network id ".*HTTP.*", // match only HTTP - "/output/ngrep-http.out" // this last parameter will be used as output file + "/output/ngrep-http.out", // this last parameter will be used as output file ]) - .start() + .start(); } - unleashServer = require('./servers/index').create(config, network) - await unleashServer.initialize() - initialized = true + unleashServer = require("./servers/index").create(config, network); + await unleashServer.initialize(); + initialized = true; } else { console.log( `===== Reseting Unleash ${server.type} before ${definition.name} =====` - ) - await unleashServer.reset() + ); + await unleashServer.reset(); } // ========= set unleash state (~50ms) - let succeed = await unleashServer.setState(definition.state) - expect(succeed).toBeTruthy() - }) + let succeed = await unleashServer.setState(definition.state); + expect(succeed).toBeTruthy(); + }); - describe.each(sdks)(`$name SDK`, sdkTestConfig => { - const excludedForSDK = sdkTestConfig.excluding || [] - let sdkUrl: string + describe.each(sdks)(`$name SDK`, (sdkTestConfig) => { + const excludedForSDK = sdkTestConfig.excluding || []; + let sdkUrl: string; + console.log(`${JSON.stringify(sdkTestConfig)}`); beforeAll(async () => { - let sdkContainer = sdkContainers.get(sdkTestConfig.type) + let sdkContainer = sdkContainers.get(sdkTestConfig.type); if (!sdkContainer) { // console.log(`===== Initializing ${sdk} =====`) let options: SDKOptions = { unleashApiUrl: `http://${unleashServer.getInternalIpAddress()}:${unleashServer.getInternalPort()}/api`, apiToken: config.unleash.adminToken, network: network, - sdkImpl: sdkTestConfig.client - } - let { create } = require(`./sdks/${sdkTestConfig.type}/container`) - sdkContainer = create(options) as ContainerInstance - await sdkContainer.initialize() - sdkContainers.set(sdkTestConfig.name!, sdkContainer) + sdkImpl: sdkTestConfig.client, + }; + let { create } = require(`./sdks/${sdkTestConfig.type}/container`); + sdkContainer = create(options) as ContainerInstance; + await sdkContainer.initialize(); + sdkContainers.set(sdkTestConfig.name!, sdkContainer); } else { // cleanup cached SDK state // console.log(`===== Reseting state of ${sdk} =====`) - await sdkContainer.reset() + await sdkContainer.reset(); } - sdkUrl = `http://localhost:${sdkContainer.getMappedPort()}` - }) - - const splitFn = (t: ITestDef) => excludedForSDK.filter(s => testName.startsWith(s) || t.description.startsWith(s)).length > 0 - const [skip, evaluate] = partition(definition.tests || [], splitFn) + sdkUrl = `http://localhost:${sdkContainer.getMappedPort()}`; + }); + + const splitFn = (t: ITestDef) => + excludedForSDK.filter( + (s) => testName.startsWith(s) || t.description.startsWith(s) + ).length > 0; + const [skip, evaluate] = partition(definition.tests || [], splitFn); + console.log(`=== ${definition.name} TO SKIP: ${JSON.stringify(skip)}`); if (skip.length > 0) test.skip.each(skip)(`$description`, () => {}); if (evaluate.length > 0) { - test.each(evaluate)(`$description`, async testCase => { + test.each(evaluate)(`$description`, async (testCase) => { const { body, statusCode } = await got.post(`${sdkUrl}/is-enabled`, { json: { toggle: testCase.toggleName, - context: testCase.context - } - }) - expect(statusCode).toBe(200) - const result: EnabledResult = JSON.parse(body) - expect(result.enabled).toBe(testCase.expectedResult) - }) + context: testCase.context, + }, + }); + expect(statusCode).toBe(200); + const result: EnabledResult = JSON.parse(body); + expect(result.enabled).toBe(testCase.expectedResult); + }); } - const [skipVariants, evaluateVariants] = partition(definition.variantTests || [], splitFn) - if (skipVariants.length > 0) test.skip.each(skipVariants)(`$description`, () => {}); + const [skipVariants, evaluateVariants] = partition( + definition.variantTests || [], + splitFn + ); + if (skipVariants.length > 0) + test.skip.each(skipVariants)(`$description`, () => {}); if (evaluateVariants.length > 0) { - test.each(evaluateVariants)(`$description`, async testCase => { + test.each(evaluateVariants)(`$description`, async (testCase) => { const { body, statusCode } = await got.post(`${sdkUrl}/variant`, { json: { toggle: testCase.toggleName, - context: testCase.context - } - }) - expect(statusCode).toBe(200) - const variantResult: VariantResult = JSON.parse(body) - const result = parseResult(sdkTestConfig.name!, variantResult) - expect(result).toEqual(testCase.expectedResult) - }) + context: testCase.context, + }, + }); + expect(statusCode).toBe(200); + const variantResult: VariantResult = JSON.parse(body); + const result = parseResult(sdkTestConfig.name!, variantResult); + expect(result).toEqual( + expect.objectContaining(testCase.expectedResult) + ); + }); } - }) - }) -}) + }); + }); +}); diff --git a/src/only-valid.yaml b/src/only-valid.yaml index dd8e292..ee99d5e 100644 --- a/src/only-valid.yaml +++ b/src/only-valid.yaml @@ -5,18 +5,12 @@ servers: sdks: - type: node - type: python - excluding: - - 'Feature.constraints.dual should be enabled in prod' - - 'Enabled in prod for userId=123' - - 'Feature.constraints.custom should be enabled in prod for norway' - - 'Feature.constraints.multi should be enabled in prod for user 2' - - 'Feature.constraints.list should be enabled in stage environment' - - 'Feature.constraints.simple should be enabled in dev environment' - - '12' - - '13' - - '15' - type: java client: io.getunleash:unleash-client-java:6.1.0 + - type: java + client: io.getunleash:unleash-client-java:7.1.0 + - type: java + client: io.getunleash:unleash-client-java:8.3.0 - type: go excluding: - '08' diff --git a/src/sdks/dotnet/dotnet.csproj b/src/sdks/dotnet/dotnet.csproj index de664bd..d3f6b98 100644 --- a/src/sdks/dotnet/dotnet.csproj +++ b/src/sdks/dotnet/dotnet.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/sdks/go/go.mod b/src/sdks/go/go.mod index 832bca3..93bdda3 100644 --- a/src/sdks/go/go.mod +++ b/src/sdks/go/go.mod @@ -1,7 +1,7 @@ module github.com/Unleash/sdk-integration-tester require ( - github.com/Unleash/unleash-client-go/v3 v3.7.0 + github.com/Unleash/unleash-client-go/v3 v3.8.0 github.com/gin-gonic/gin v1.9.0 ) @@ -9,6 +9,7 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/bytedance/sonic v1.8.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -21,6 +22,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/testify v1.8.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twmb/murmur3 v1.1.5 // indirect github.com/ugorji/go/codec v1.2.9 // indirect diff --git a/src/sdks/go/go.sum b/src/sdks/go/go.sum index e2721f4..44db9a4 100644 --- a/src/sdks/go/go.sum +++ b/src/sdks/go/go.sum @@ -2,6 +2,8 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Unleash/unleash-client-go/v3 v3.7.0 h1:N0NYPCXEYw04U84d9wUgP9vrYijLqpOYsa1XbD+9Vqk= github.com/Unleash/unleash-client-go/v3 v3.7.0/go.mod h1:jAf7F2WWpfJbfn1n8bZ74p7hkAhijrqH4TpWoT7kWLc= +github.com/Unleash/unleash-client-go/v3 v3.8.0 h1:DS2yU0/ouy8lXGO3FzocUTZMyO7uhjHPEYmwqGEJopQ= +github.com/Unleash/unleash-client-go/v3 v3.8.0/go.mod h1:jAf7F2WWpfJbfn1n8bZ74p7hkAhijrqH4TpWoT7kWLc= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= diff --git a/src/sdks/node/Dockerfile b/src/sdks/node/Dockerfile index be47c4a..d65273b 100644 --- a/src/sdks/node/Dockerfile +++ b/src/sdks/node/Dockerfile @@ -1,11 +1,11 @@ -FROM node:16-alpine as builder +FROM node:20-alpine as builder WORKDIR /unleash COPY package.json index.js /unleash/ RUN yarn install --frozen-lockfile --production=true -FROM node:16-alpine +FROM node:20-alpine ENV NODE_ENV production diff --git a/src/sdks/node/package.json b/src/sdks/node/package.json index 11cf45b..e7a2e6f 100644 --- a/src/sdks/node/package.json +++ b/src/sdks/node/package.json @@ -11,6 +11,6 @@ "dependencies": { "fastify": "^3.29.0", "leekslazylogger-fastify": "^0.1.1", - "unleash-client": "^3.13.4" + "unleash-client": "5.5.5" } } diff --git a/src/sdks/python/requirements.txt b/src/sdks/python/requirements.txt index 847ee75..de68faf 100644 --- a/src/sdks/python/requirements.txt +++ b/src/sdks/python/requirements.txt @@ -4,7 +4,7 @@ importlib-metadata==4.11.3 itsdangerous==2.1.2 Jinja2==3.1.4 gunicorn==22.0.0 -UnleashClient==5.4.0 +UnleashClient==5.8.0 MarkupSafe==2.1.1 Werkzeug==2.3.8 zipp==3.8.0