Skip to content

Commit

Permalink
feat(types): use joi schema instead of typescript (#2122)
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardobridge authored Sep 12, 2023
1 parent a77a3e9 commit d323874
Show file tree
Hide file tree
Showing 21 changed files with 1,280 additions and 455 deletions.
379 changes: 21 additions & 358 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions packages/types/generate-schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs = require('fs');
const path = require('path');
const parse = require('joi-to-json');
const { schema } = require('./schema/index');

const jsonSchema = parse(schema);

fs.writeFileSync(
path.join(__dirname, './schema.json'),
JSON.stringify(jsonSchema, null, 2)
);
4 changes: 3 additions & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
"scripts": {
"pretest": "npm run build",
"test": "tap --ts --no-coverage --color --timeout 60 test/**/*.test.ts",
"build": "typescript-json-schema ./tsconfig.schema.json \"TestScript\" --required --noExtraProps -o ./schema.json",
"build": "node generate-schema.js",
"prepare": "npm run build",
"prepublishOnly": "npm run build && npm test"
},
"devDependencies": {
"@types/tap": "^15.0.8",
"ajv": "^8.12.0",
"joi": "^17.6.0",
"joi-to-json": "^4.0.1",
"js-yaml": "^4.1.0",
"tap": "^16.3.8",
"ts-node": "^10.9.1",
Expand Down
175 changes: 175 additions & 0 deletions packages/types/schema/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
const Joi = require('joi').defaults((schema) =>
schema.options({ allowUnknown: true, abortEarly: true })
);
const {
artilleryNumberOrString,
artilleryBooleanOrString
} = require('./joi.helpers');
const { HttpConfigSchema } = require('./engines/http');
const { WsConfigSchema } = require('./engines/websocket');
const { SocketIoConfigSchema } = require('./engines/socketio');
const { PlaywrightConfigSchema } = require('./engines/playwright');
const { ExpectPluginConfigSchema } = require('./plugins/expect');
const { EnsurePluginConfigSchema } = require('./plugins/ensure');
const { ApdexPluginConfigSchema } = require('./plugins/apdex');
const {
MetricsByEndpointPluginConfigSchema
} = require('./plugins/metrics-by-endpoint');
const {
PublishMetricsPluginConfigSchema
} = require('./plugins/publish-metrics');

const TlsConfig = Joi.object({
rejectUnauthorized: Joi.boolean().meta({
title:
'Set this setting to `false` to tell Artillery to accept self-signed TLS certificates.'
})
});

const TestPhaseWithArrivals = Joi.object({
name: Joi.string().meta({ title: 'Test Phase Name' }),
duration: artilleryNumberOrString
.meta({ title: 'Test Phase Duration' })
.description(
'Test phase duration (in seconds).\nCan also be any valid human-readable duration: https://www.npmjs.com/package/ms .'
),
arrivalRate: artilleryNumberOrString
.meta({ title: 'Arrival Rate' })
.description(
'Constant arrival rate.\nThe number of virtual users generated every second.'
),
arrivalCount: artilleryNumberOrString
.meta({ title: 'Arrival Count' })
.description(
'Fixed number of virtual users over that time period.\nhttps://www.artillery.io/docs/reference/test-script#fixed-number-of-arrivals-per-second'
),
rampTo: artilleryNumberOrString
.meta({ title: 'Ramp up rate' })
.description(
'Ramp from initial arrivalRate to this value over time period.\nhttps://www.artillery.io/docs/reference/test-script#ramp-up-rate'
),
maxVusers: artilleryNumberOrString
.meta({ title: 'Maximum virtual users' })
.description(
'Cap the number of concurrent virtual users at any given time.'
)
});

const TestPhaseWithPause = Joi.object({
name: Joi.string().meta({ title: 'Test Phase Name' }),
pause: artilleryNumberOrString
.meta({ title: 'Pause' })
.description(
'Pause the test phase execution for given duration (in seconds).\nCan also be any valid human-readable duration: https://www.npmjs.com/package/ms.'
)
});

const TestPhase = Joi.alternatives(
TestPhaseWithArrivals,
TestPhaseWithPause
).meta({ title: 'Test Phase' });

const PayloadConfig = Joi.object({
path: Joi.string().meta({ title: 'CSV Path' }),
fields: Joi.array()
.items(Joi.string())
.meta({ title: 'CSV Fields' })
.description(
'List of names of fields to be used in the test to load the data'
),
order: Joi.alternatives('random', 'sequence')
.meta({ title: 'Order' })
.description(
'Controls how the CSV rows are selected for each virtual user.'
),
skipHeader: artilleryBooleanOrString
.meta({ title: 'Skip Header?' })
.description(
'Set to `true` to make Artillery skip the first row in the CSV file (typically the header row)'
), //TODO: add default
delimiter: Joi.string()
.meta({ title: 'Delimiter' })
.description('Custom delimiter character to use in the payload.'), //TODO: add default
cast: artilleryBooleanOrString
.meta({ title: 'Cast?' })
.description(
'Controls whether Artillery converts fields to native types (e.g. numbers or booleans). To keep those fields as strings, set this option to `false`.'
),
skipEmptyLines: artilleryBooleanOrString
.meta({ title: 'Skip empty lines?' })
.description(
'Controls whether Artillery should skip empty lines in the payload.'
),
loadAll: artilleryBooleanOrString
.meta({ title: 'Load all data' })
.description('Set loadAll to true to provide all rows to each VU'),
name: Joi.string()
.meta({ title: 'Data Name' })
.description('Name of loadAll data') //TODO: loadAll and name used conditionally
});

const ReplaceableConfig = {
target: Joi.string()
.meta({ title: 'Target' })
.description(
'Endpoint of the system under test, such as a hostname, IP address or a URI.\nhttps://www.artillery.io/docs/reference/test-script#target---target-service'
)
.example('https://example.com')
.example('ws://127.0.0.1'),
phases: Joi.array()
.items(TestPhase)
.meta({ title: 'Phases' })
.description(
'A load phase defines how Artillery generates new virtual users (VUs) in a specified time period.\nhttps://www.artillery.io/docs/reference/test-script#phases---load-phases'
)
};

const ArtilleryBuiltInPlugins = {
expect: ExpectPluginConfigSchema,
ensure: EnsurePluginConfigSchema,
apdex: ApdexPluginConfigSchema,
'metrics-by-endpoint': MetricsByEndpointPluginConfigSchema,
'publish-metrics': PublishMetricsPluginConfigSchema
};

const ConfigSchema = Joi.object({
...ReplaceableConfig,
http: HttpConfigSchema.meta({ title: 'HTTP Configuration' }),
ws: WsConfigSchema.meta({ title: 'Websocket Configuration' }),
socketio: SocketIoConfigSchema.meta({ title: 'SocketIo Configuration' }),
environments: Joi.object()
// .rename(/\w\d/, 'something')
// .pattern(/\w\d/, Joi.object(ReplaceableConfig))//TODO: this isn't working well. Probably a limitation of https://github.com/kenspirit/joi-to-json#known-limitation. Find alternative?
.meta({ title: 'Environments' })
.description(
'Define environments to run your load test against different configs:\nhttps://www.artillery.io/docs/reference/test-script#environments---config-profiles'
), //TODO: type this properly

processor: Joi.string()
.meta({ title: 'Processor Function Path' })
.description('Path to a CommonJS module to load for this test run.'),
variables: Joi.object()
.meta({ title: 'Variables' })
.description('Map of variables to expose to the test run.'),
payload: Joi.alternatives(PayloadConfig, Joi.array().items(PayloadConfig))
.meta({ title: 'CSV Payload' })
.description(
'Load data from CSV to be used during the test run:\nhttps://www.artillery.io/docs/reference/test-script#payload---loading-data-from-csv-files'
),
tls: TlsConfig.meta({ title: 'TLS Settings' }),
plugins: Joi.object({ ...ArtilleryBuiltInPlugins })
.meta({ title: 'Plugins' })
.description(
'List of Artillery plugins to use (official or third-party) and their configuration'
),
engines: Joi.object({
playwright: PlaywrightConfigSchema
})
.meta({ title: 'Engines' })
.description('Configuration for specific engines used'),
...ArtilleryBuiltInPlugins
});

module.exports = {
ConfigSchema
};
46 changes: 46 additions & 0 deletions packages/types/schema/engines/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const Joi = require('joi').defaults((schema) =>
schema.options({ allowUnknown: true, abortEarly: true })
);

const { artilleryNumberOrString } = require('../joi.helpers');

const BaseFlowItemAlternatives = [
Joi.object({
function: Joi.string()
.meta({ title: 'Function' })
.description('Function name to run.')
}),
Joi.object({
log: Joi.string()
.meta({ title: 'Log' })
.description('Print given message to the console.')
}),
Joi.object({
think: artilleryNumberOrString
.meta({ title: 'Think time' })
.description('Pause virtual user for the given duration (in seconds).')
})
];

const LoopOptions = {
whileTrue: Joi.string()
.meta({ title: 'Loop While True' })
.description(
'Control the loop using custom logic:\nhttps://www.artillery.io/docs/reference/engines/http#looping-through-an-array'
),
count: artilleryNumberOrString
.meta({ title: 'Loop N times' })
.description(
'https://www.artillery.io/docs/reference/engines/http#looping-through-an-array'
),
over: Joi.alternatives(Joi.string(), Joi.array().items(Joi.string()))
.meta({ title: 'Loop over array' })
.description(
'https://www.artillery.io/docs/reference/engines/http#looping-through-an-array'
)
};

module.exports = {
BaseFlowItemAlternatives,
LoopOptions
};
Loading

0 comments on commit d323874

Please sign in to comment.