Files
directus/tests/blackbox/common/seed-functions.ts
rijkvanzanten 37658802b7 One more
2023-04-14 17:30:56 -04:00

400 lines
11 KiB
TypeScript

import { v5 as uuid } from 'uuid';
import * as seedrandom from 'seedrandom';
import { PrimaryKeyType } from '@common/types';
const SEED_UUID_NAMESPACE = 'e81a0012-568b-415c-96fa-66508f594067';
const FIVE_YEARS_IN_MILLISECONDS = 5 * 365 * 24 * 60 * 60 * 1000;
type OptionsSeedGenerateBase = {
quantity: number;
seed?: string;
vendor?: string;
isDefaultValue?: boolean;
};
export type OptionsSeedGeneratePrimaryKeys = OptionsSeedGenerateBase & OptionsSeedGenerateInteger;
export type OptionsSeedGenerateInteger = OptionsSeedGenerateBase & {
startsAt?: number;
incremental?: boolean;
};
export type OptionsSeedGenerateBigInteger = OptionsSeedGenerateBase;
export type OptionsSeedGenerateString = OptionsSeedGenerateBase & {
startsWith?: string;
endsWith?: string;
};
export type OptionsSeedGenerateUUID = OptionsSeedGenerateBase;
export type OptionsSeedGenerateBoolean = OptionsSeedGenerateBase;
export type OptionsSeedGenerateCSV = OptionsSeedGenerateBase;
export type OptionsSeedGenerateDate = OptionsSeedGenerateBase & {
startsFrom?: Date | string;
endsOn?: Date | string;
};
export type OptionsSeedGenerateDateTime = OptionsSeedGenerateTimestamp;
export type OptionsSeedGenerateDecimal = OptionsSeedGenerateFloat;
export type OptionsSeedGenerateFloat = OptionsSeedGenerateBase & {
startsAt?: number;
endsAt?: number;
amount?: number;
precision?: number;
};
export type OptionsSeedGenerateGeometry = OptionsSeedGenerateBase;
export type OptionsSeedGenerateHash = OptionsSeedGenerateBase;
export type OptionsSeedGenerateJSON = OptionsSeedGenerateBase;
export type OptionsSeedGenerateTime = OptionsSeedGenerateBase & {
startsFrom?: Date | string;
endsOn?: Date | string;
};
export type OptionsSeedGenerateTimestamp = OptionsSeedGenerateBase & {
startsFrom?: Date;
endsOn?: Date;
};
export const SeedFunctions = {
generatePrimaryKeys,
generateValues: {
integer: generateInteger,
bigInteger: generateBigInteger,
uuid: generateUUID,
string: generateString,
text: generateString,
boolean: generateBoolean,
csv: generateCSV,
date: generateDate,
dateTime: generateDateTime,
decimal: generateDecimal,
float: generateFloat,
// geometry: null,
// hash: null,
// json: null,
time: generateTime,
timestamp: generateTimestamp,
},
};
function generatePrimaryKeys(pkType: PrimaryKeyType, options: OptionsSeedGeneratePrimaryKeys) {
switch (pkType) {
case 'integer':
return generateInteger(options);
case 'uuid':
return generateUUID(options);
case 'string':
return generateString(options);
default:
throw new Error(`Unsupported primary key type: ${pkType}`);
}
}
function generateInteger(options: OptionsSeedGenerateInteger) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
if (options.incremental) {
const randomStartValue = random.int32();
for (let i = 0; i < options.quantity; i++) {
if (options.startsAt) {
values.push(options.startsAt + i);
} else {
values.push(randomStartValue + i);
}
}
} else {
for (let i = 0; i < options.quantity; i++) {
if (options.startsAt) {
values.push(random.int32() + options.startsAt + i);
} else {
values.push(random.int32() + i);
}
}
}
return values;
}
function generateBigInteger(options: OptionsSeedGenerateBigInteger) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
for (let i = 0; i < options.quantity; i++) {
// TODO: Maximise array length back to 15, fix filter parsing which reduces this value
const hexString = Array(12)
.fill(0)
.map(() => Math.round(random() * 0xf).toString(16))
.join('');
values.push(BigInt(`0x7${hexString}`));
}
return values;
}
function generateFloat(options: OptionsSeedGenerateFloat) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
for (let i = 0; i < options.quantity; i++) {
if (options.startsAt && options.endsAt) {
values.push(
Number((options.startsAt + random() * (options.endsAt - options.startsAt + 1)).toFixed(options.precision ?? 3))
);
} else if (options.startsAt) {
values.push(Number((options.startsAt + random() * (options.amount ?? 100)).toFixed(options.precision ?? 3)));
} else if (options.endsAt) {
values.push(Number((options.endsAt - random() * (options.amount ?? 100)).toFixed(options.precision ?? 3)));
} else {
values.push(Number((random() * (options.amount ?? 100)).toFixed(options.precision ?? 3)));
}
}
return values;
}
function generateDecimal(options: OptionsSeedGenerateDecimal) {
return generateFloat(options);
}
function generateString(options: OptionsSeedGenerateString) {
const values = [];
for (let i = 0; i < options.quantity; i++) {
let value = uuid(String((options.seed ?? 'default') + i), SEED_UUID_NAMESPACE).replace(/-/g, '');
if (options.startsWith) {
value = options.startsWith + value;
}
if (options.endsWith) {
value = value + options.endsWith;
}
values.push(value);
}
return values;
}
function generateUUID(options: OptionsSeedGenerateUUID) {
const values = [];
for (let i = 0; i < options.quantity; i++) {
values.push(uuid(String((options.seed ?? 'default') + i), SEED_UUID_NAMESPACE));
}
return values;
}
function generateBoolean(options: OptionsSeedGenerateBoolean) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
for (let i = 0; i < options.quantity; i++) {
values.push(random() >= 0.5);
}
// Ensure that there is always true and false values to test on
if (options.quantity >= 2) {
values[0] = true;
values[1] = false;
}
return values;
}
function generateCSV(options: OptionsSeedGenerateCSV) {
const values = [];
for (let i = 0; i < options.quantity; i++) {
values.push(uuid(String((options.seed ?? 'default') + i), SEED_UUID_NAMESPACE));
}
return values;
}
function generateDate(options: OptionsSeedGenerateDate) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
if (typeof options.startsFrom === 'string') {
options.startsFrom = new Date(Date.parse(options.startsFrom));
}
if (typeof options.endsOn === 'string') {
options.endsOn = new Date(Date.parse(options.endsOn));
}
if (options.startsFrom && options.endsOn) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(
Math.floor(
random() * (options.endsOn.getTime() - options.startsFrom.getTime() + 1) + options.startsFrom.getTime()
)
)
.toISOString()
.substring(0, 10)
);
}
} else if (options.startsFrom) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(Math.floor(random() * FIVE_YEARS_IN_MILLISECONDS + options.startsFrom.getTime()))
.toISOString()
.substring(0, 10)
);
}
} else if (options.endsOn) {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * options.endsOn.getTime())).toISOString().substring(0, 10));
}
} else {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * FIVE_YEARS_IN_MILLISECONDS)).toISOString().substring(0, 10));
}
}
if (options.isDefaultValue && options.vendor === 'oracle') {
for (let i = 0; i < values.length; i++) {
values[i] = new Date(values[i])
.toLocaleDateString('en-GB', {
day: 'numeric',
month: 'short',
year: 'numeric',
})
.replace(/ /g, '-');
}
}
return values;
}
function generateDateTime(options: OptionsSeedGenerateDateTime) {
const values = generateTimestamp(options);
for (let i = 0; i < values.length; i++) {
values[i] = values[i].slice(0, -5);
}
if (options.isDefaultValue && options.vendor === 'oracle') {
for (let index = 0; index < values.length; index++) {
values[index] = 'CURRENT_TIMESTAMP';
}
}
return values;
}
function generateTime(options: OptionsSeedGenerateTime) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
let timeStart = options.startsFrom;
let timeEnd = options.endsOn;
if (typeof timeStart === 'string') {
timeStart = new Date(`1970-01-01T${timeStart}`);
}
if (typeof timeEnd === 'string') {
timeEnd = new Date(`1970-01-01T${timeEnd}`);
}
if (timeStart && timeEnd) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(Math.floor(random() * (timeEnd.getTime() - timeStart.getTime() + 1) + timeStart.getTime()))
.toISOString()
.slice(11, 19)
);
}
} else if (timeStart) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(Math.floor(random() * (86400000 /* 24h */ - timeStart.getTime()) + timeStart.getTime()))
.toISOString()
.slice(11, 19)
);
}
} else if (timeEnd) {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * timeEnd.getTime())).toISOString().slice(11, 19));
}
} else {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * FIVE_YEARS_IN_MILLISECONDS)).toISOString().slice(11, 19));
}
}
if (options.isDefaultValue && options.vendor === 'oracle') {
for (let index = 0; index < values.length; index++) {
values[index] = 'CURRENT_TIMESTAMP';
}
}
return values;
}
function generateTimestamp(options: OptionsSeedGenerateTimestamp) {
const random = seedrandom.xor128(options.seed ?? 'default');
const values = [];
if (options.startsFrom && options.endsOn) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(
Math.floor(
random() * (options.endsOn.getTime() - options.startsFrom.getTime() + 1) + options.startsFrom.getTime()
)
).toISOString()
);
}
} else if (options.startsFrom) {
for (let i = 0; i < options.quantity; i++) {
values.push(
new Date(Math.floor(random() * FIVE_YEARS_IN_MILLISECONDS + options.startsFrom.getTime())).toISOString()
);
}
} else if (options.endsOn) {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * options.endsOn.getTime())).toISOString());
}
} else {
for (let i = 0; i < options.quantity; i++) {
values.push(new Date(Math.floor(random() * FIVE_YEARS_IN_MILLISECONDS)).toISOString());
}
}
// Overcome MySQL / Maria created without decimal accuracy
// Overcome MSSQL specific accuracy up to 1/300th of a second
for (let index = 0; index < values.length; index++) {
values[index] = values[index].slice(0, 20) + '000Z';
}
if (options.isDefaultValue && options.vendor) {
if (['mysql', 'mysql5', 'maria'].includes(options.vendor)) {
for (let index = 0; index < values.length; index++) {
values[index] = new Date(values[index]).toISOString().replace(/([^T]+)T([^.]+).*/g, '$1 $2');
}
} else if (options.vendor === 'oracle') {
for (let index = 0; index < values.length; index++) {
values[index] = 'CURRENT_TIMESTAMP';
}
}
}
return values;
}