Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LW-11271 Utils for K6 load tests #1424

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/k6-web-socket.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: K6 WebSocket server load tests

on:
workflow_dispatch:
branches: ['master', 'feat/web-socket-network-info-provider']
inputs:
environment:
description: 'Target environment'
Expand All @@ -12,7 +11,7 @@ on:
- 'dev'
- 'ops'
- 'staging'
- 'prod'
- 'live'
network:
description: 'Target network'
type: choice
Expand All @@ -34,6 +33,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: 🧰 Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18.12.0
- name: 🔨 Build
run: |
yarn install --immutable --inline-builds --mode=skip-build
yarn workspace @cardano-sdk/util-dev build:cjs
env:
NODE_OPTIONS: '--max_old_space_size=8192'
- name: Run k6 cloud test
uses: grafana/k6-action@v0.3.1
env:
Expand Down
19 changes: 9 additions & 10 deletions packages/e2e/test/k6/scenarios/wallets.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable func-style */
import { Counter, Trend } from 'k6/metrics';
import { SharedArray } from 'k6/data';
import { check, sleep } from 'k6';
import { apiVersion } from '../../../../cardano-services-client/src/version.ts';
import { check, sleep } from 'k6';
import http from 'k6/http';

/**
Expand Down Expand Up @@ -42,22 +43,19 @@ const RUN_MODE = __ENV.RUN_MODE || RunMode.Restore;
const PROVIDER_SERVER_URL = __ENV.PROVIDER_SERVER_URL;

/** Wallet addresses extracted from the JSON dump file */
const walletsOrig = new SharedArray('walletsData', function () {
const network = __ENV.TARGET_ENV == 'dev-mainnet' ? 'mainnet' : 'preprod';
const walletsOrig = new SharedArray('walletsData', () => {
const network = __ENV.TARGET_ENV === 'dev-mainnet' ? 'mainnet' : 'preprod';
const fileName = RUN_MODE === RunMode.Onboard ? `no-history-${network}.json` : `${network}.json`;
console.log(`Reading wallet addresses from ${fileName}`);
const walletAddresses = JSON.parse(open('../../dump/addresses/' + fileName));
return walletAddresses;
return JSON.parse(open(`../../dump/addresses/${fileName}`));
});


/** Stake pool addresses from the JSON dump file */
const poolAddresses = new SharedArray('poolsData', function () {
const poolAddresses = new SharedArray('poolsData', () =>
// There is no dump of preprod pools, but it is ok. Pool address is used only in "Restore" mode
// and it is used to do a stake pool search call, so any pool will do
const pools = JSON.parse(open('../../dump/pool_addresses/mainnet.json'));
return pools;
});
JSON.parse(open('../../dump/pool_addresses/mainnet.json'))
);

/**
* Define the maximum number of virtual users to simulate
Expand Down Expand Up @@ -314,6 +312,7 @@ const emulateIdleClient = () => cardanoHttpPost(TIP_URL, apiVersion.networkInfo)
* wallets: {address: Cardano.Address, stake_address: Cardano.RewardAccount, tx_count: number}[][]
* poolAddresses: Cardano.PoolId[]
*/
// eslint-disable-next-line @typescript-eslint/no-shadow
export default function ({ wallets, poolAddresses }) {
// Get the wallet for the current virtual user
// eslint-disable-next-line no-undef
Expand Down
15 changes: 6 additions & 9 deletions packages/e2e/test/k6/scenarios/web-socket.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
// cSpell:ignore loadimpact

import * as k6Utils from '../../../../util-dev/dist/cjs/k6-utils.js';
import { Counter, Trend } from 'k6/metrics';
import { check } from 'k6';
import ws from 'k6/ws';

// eslint-disable-next-line no-undef
const { TARGET_ENV, TARGET_NET, URL, WALLETS } = Object.assign({ WALLETS: '10000' }, __ENV);
const url = TARGET_ENV ? `wss://${TARGET_ENV}-${TARGET_NET}${TARGET_ENV === 'ops' ? '-1' : ''}.lw.iog.io/ws` : URL;
const { WALLETS } = Object.assign({ WALLETS: '10000' }, __ENV);
// eslint-disable-next-line no-undef
const dut = k6Utils.getDut(__ENV);
console.log(`Domain under test is: ${dut}`);

if (TARGET_ENV && !['dev', 'ops', 'staging', 'prod'].includes(TARGET_ENV))
throw new Error(`Not valid TARGET_ENV: ${TARGET_ENV}`);
if (TARGET_NET && !['preview', 'preprod', 'sanchonet', 'mainnet'].includes(TARGET_NET))
throw new Error(`Not valid TARGET_NET: ${TARGET_NET}`);
if (!(TARGET_ENV && TARGET_NET) && !URL) throw new Error('Please specify both TARGET_ENV and TARGET_NET or URL');
const url = `wss://${dut}/ws`;

export const options = {
ext: {
Expand Down
97 changes: 97 additions & 0 deletions packages/util-dev/src/k6-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* eslint-disable sonarjs/cognitive-complexity */
export enum Environment {
dev = 'dev',
ops = 'ops',
staging = 'staging',
live = 'live'
}

export enum Network {
mainnet = 'mainnet',
preprod = 'preprod',
preview = 'preview',
sanchonet = 'sanchonet'
}

/**
* Gets the target URL domain under test (dut) based on `TARGET_ENV`, `TARGET_NET` or `DUT` environment variables.
*

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.TARGET_ENV"

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.TARGET_NET"

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.DUT"

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.TARGET_ENV"

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.TARGET_NET"

Check warning on line 18 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Missing @param "k6Env.DUT"
* @param options default `undefined`: use default value for each option.<br />

Check warning on line 19 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

@param "options" does not match parameter name "k6Env"

Check warning on line 19 in packages/util-dev/src/k6-utils.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

@param "options" does not match parameter name "k6Env"
* - `environments`: the array of allowed environments - default: `['dev', 'ops', 'staging', 'prod']`.
* - `networks`: the array of allowed networks - default: `['mainnet', 'preprod', 'preview', 'sanchonet']`.
* @returns the domain under test (dut) based on the environment variables, (e.g. `live-preprod.lw.iog.io`)
* @throws if none of `TARGET_ENV` and `TARGET_NET`, or `DUT` are configured, or if the options are invalid.
*/
// eslint-disable-next-line complexity, sonarjs/cognitive-complexity
export const getDut = (
k6Env: { TARGET_ENV?: string; TARGET_NET?: string; DUT?: string },
options?: { environments?: Environment[]; networks?: Network[] }
) => {
const allowedEnvironments = new Set<Environment>([
Environment.dev,
Environment.ops,
Environment.staging,
Environment.live
]);
const allowedNetworks = new Set<Network>([Network.mainnet, Network.preprod, Network.preview, Network.sanchonet]);
const allowedOptions = new Set(['environments', 'networks']);

const { TARGET_ENV, TARGET_NET, DUT } = k6Env;

if (!(TARGET_ENV && TARGET_NET) && !DUT)
throw new Error('Please specify both TARGET_ENV and TARGET_NET or DUT (Domain Under Test');

let urlEnvironments = [...allowedEnvironments];
let urlNetworks = [...allowedNetworks];

if (options) {
if (typeof options !== 'object') throw new Error(`${typeof options}: not allowed type for options`);

for (const option of Object.keys(options))
if (!allowedOptions.has(option)) throw new Error(`${options}: not allowed option`);

const { environments, networks } = options;

switch (typeof environments) {
case 'undefined':
break;

case 'object':
if (!Array.isArray(environments)) throw new Error('options.environments must be an array');

for (const environment of environments)
if (!allowedEnvironments.has(environment)) throw new Error(`${environment}: not allowed environment`);

urlEnvironments = environments;

break;

default:
throw new Error(`${typeof environments}: not allowed type for options.environments`);
}

switch (typeof networks) {
case 'undefined':
break;

case 'object':
if (!Array.isArray(networks)) throw new Error('options.networks must be an array');

for (const network of networks)
if (!allowedNetworks.has(network)) throw new Error(`${network}: not allowed network`);

urlNetworks = networks;

break;

default:
throw new Error(`${typeof networks}: not allowed type for options.networks`);
}
}

if (TARGET_ENV && !urlEnvironments.includes(TARGET_ENV as Environment))
throw new Error(`${TARGET_ENV}: not allowed environment`);
if (TARGET_NET && !urlNetworks.includes(TARGET_NET as Network)) throw new Error(`${TARGET_NET}: not allowed network`);

return DUT || `${TARGET_ENV}-${TARGET_NET}${TARGET_ENV === 'ops' ? '-1' : ''}.lw.iog.io`;
};
58 changes: 58 additions & 0 deletions packages/util-dev/test/k6-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as k6Utils from '../src/k6-utils';

describe('K6 utils', () => {
const k6Env = { TARGET_ENV: 'dev', TARGET_NET: 'mainnet' };

it('uses custom DUT if configured', () => {
expect(k6Utils.getDut({ DUT: 'localhost:3000' })).toBe('localhost:3000');
});

it('builds the correct domain under test based on TARGET_ENV and TARGET_NET', () => {
expect(k6Utils.getDut(k6Env)).toBe('dev-mainnet.lw.iog.io');
expect(k6Utils.getDut({ TARGET_ENV: k6Utils.Environment.live, TARGET_NET: k6Utils.Network.preprod })).toBe(
'live-preprod.lw.iog.io'
);
expect(k6Utils.getDut({ TARGET_ENV: k6Utils.Environment.ops, TARGET_NET: k6Utils.Network.preview })).toBe(
'ops-preview-1.lw.iog.io'
);
});

describe('Runtime validations', () => {
it('throws an error if none of TARGET_ENV and TARGET_NETWORK, or DUT are configured', () => {
expect(() => k6Utils.getDut({})).toThrow();
expect(() => k6Utils.getDut({ TARGET_ENV: k6Utils.Environment.dev })).toThrow();
expect(() => k6Utils.getDut({ TARGET_NET: k6Utils.Network.mainnet })).toThrow();
});

it('checks options type', () => {
expect(() => k6Utils.getDut(k6Env, 'invalid' as any)).toThrow();

Check warning on line 28 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type

Check warning on line 28 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type
expect(() => k6Utils.getDut(k6Env, { unknownProp: 'invalid' } as any)).toThrow();

Check warning on line 29 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type

Check warning on line 29 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type
});

it('checks options.environments and options.network to be array', () => {
expect(() => k6Utils.getDut(k6Env, { environments: 'invalid' as any })).toThrow();

Check warning on line 33 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type

Check warning on line 33 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type
expect(() => k6Utils.getDut(k6Env, { networks: 'invalid' as any })).toThrow();

Check warning on line 34 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type

Check warning on line 34 in packages/util-dev/test/k6-utils.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

Unexpected any. Specify a different type
});

it('checks options.environments and options.network to be valid', () => {
expect(() => k6Utils.getDut(k6Env, { environments: ['invalid' as k6Utils.Environment] })).toThrow();
expect(() => k6Utils.getDut(k6Env, { networks: ['invalid' as k6Utils.Network] })).toThrow();
});

it('checks TARGET_ENV and TARGET_NET to be from options allowed values', () => {
expect(() =>
k6Utils.getDut(
{ TARGET_ENV: 'dev', TARGET_NET: 'mainnet' },
{ environments: [k6Utils.Environment.ops], networks: [k6Utils.Network.mainnet] }
)
).toThrow();

expect(() =>
k6Utils.getDut(
{ TARGET_ENV: 'dev', TARGET_NET: 'mainnet' },
{ environments: [k6Utils.Environment.dev], networks: [k6Utils.Network.preprod] }
)
).toThrow();
});
});
});
Loading