Skip to content

Commit

Permalink
Validate default ids
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Jan 2, 2022
1 parent cba9be6 commit d4faff0
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 44 deletions.
38 changes: 26 additions & 12 deletions src/combination/default.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getCombsDimensions } from './dimensions.js'
import { removePrefix } from './prefix.js'
import { getCombsDimensions, isDimension, DIMENSIONS } from './dimensions.js'

// The target result defines which combinations are reported.
// A history result might miss one of them:
Expand Down Expand Up @@ -51,21 +50,36 @@ const addCombDefaultIds = function (combination, targetDimensions) {
return { ...combination, dimensions }
}

const getNewDimension = function (targetDimension, dimensions) {
const { propName } = targetDimension
const getNewDimension = function ({ propName, getDefaultId }, dimensions) {
const id =
propName in dimensions
? dimensions[propName]
: getDimensionDefaultId(targetDimension)
: { id: getDefaultId(propName) }
return [propName, id]
}

const getDimensionDefaultId = function ({
// Find whether an id matches the default id pattern of a dimension.
export const isInvalidDefaultId = function (id, { propName }) {
return DIMENSIONS.some((dimension) =>
isInvalidDimensionId(id, propName, dimension),
)
}

const isInvalidDimensionId = function (id, propName, dimension) {
return (
dimension.defaultIdPrefix !== undefined &&
id.startsWith(dimension.defaultIdPrefix) &&
!isDimensionDefaultId(id, propName, dimension)
)
}

const isDimensionDefaultId = function (
id,
propName,
prefixName,
getDefaultId,
}) {
const propNameA = removePrefix(propName, prefixName)
const id = getDefaultId(propNameA)
return { id }
{ propName: dimensionPropName, prefixName, getDefaultId },
) {
return (
isDimension(propName, dimensionPropName, prefixName) &&
id === getDefaultId(propName)
)
}
59 changes: 30 additions & 29 deletions src/combination/dimensions.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,5 @@
import { hasPrefix, removePrefix } from './prefix.js'

// All dimensions. They:
// - create dimensions in runs using a cartesian product
// - can be used in `select|limit`
// - are printed when naming the combination
// - in reporters, preview bottom bar, `dev`, error messages
// - in some of those cases, `titles` are applied too
const DIMENSIONS = [
{
// Internal persisted property name.
// Also used to compute the `messageName`.
propName: 'task',
// Whether dimension's id was created by users or by plugins
createdByUser: true,
},
{
propName: 'runner',
createdByUser: false,
},
{
// Some dimensions are dynamic, i.e. can have multiple sub-dimensions.
// Instead of a `propName`, those have a common `prefixName`.
prefixName: 'system',
getDefaultId: (propName) => `main_system_${propName}`,
createdByUser: true,
},
]

// Find a dimension's order
export const findDimensionIndex = function (propName) {
return DIMENSIONS.findIndex((dimension) =>
Expand Down Expand Up @@ -68,14 +41,13 @@ const getDimension = function (
.sort()
.map((propName) => ({
propName,
prefixName,
messageName: getMessageName(propName, prefixName),
getDefaultId,
createdByUser,
}))
}

const isDimension = function (propName, dimensionPropName, prefixName) {
export const isDimension = function (propName, dimensionPropName, prefixName) {
return prefixName === undefined
? propName === dimensionPropName
: hasPrefix(propName, prefixName)
Expand All @@ -86,3 +58,32 @@ const getMessageName = function (propName, prefixName) {
? propName
: removePrefix(propName, prefixName)
}

// All dimensions. They:
// - create dimensions in runs using a cartesian product
// - can be used in `select|limit`
// - are printed when naming the combination
// - in reporters, preview bottom bar, `dev`, error messages
// - in some of those cases, `titles` are applied too
export const DIMENSIONS = [
{
// Internal persisted property name.
// Also used to compute the `messageName`.
propName: 'task',
// Whether dimension's id was created by users or by plugins
createdByUser: true,
},
{
propName: 'runner',
createdByUser: false,
},
{
// Some dimensions are dynamic, i.e. can have multiple sub-dimensions.
// Instead of a `propName`, those have a common `prefixName`.
prefixName: 'system',
getDefaultId: (propName) =>
`main_system_${removePrefix(propName, 'system')}`,
defaultIdPrefix: 'main_system_',
createdByUser: true,
},
]
18 changes: 15 additions & 3 deletions src/combination/ids/validate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UserError } from '../../error/main.js'
import { isInvalidDefaultId } from '../default.js'

import { getCombsDimensionsIds } from './get.js'
import { getUserIds } from './user.js'
Expand Down Expand Up @@ -62,8 +63,19 @@ const validateReservedIds = function (id, messageName) {
// 'not' and 'and' are used by `select`
const RESERVED_IDS = ['not', 'and']

const validateDimensionId = function ({ dimension, id }, index, allIds) {
validateDuplicateId({ dimension, id, index, allIds })
const validateDimensionId = function ({ id, dimension }, index, allIds) {
validateDefaultId(id, dimension)
validateDuplicateId({ id, dimension, index, allIds })
}

// Since ids must not be duplicate across dimensions, we need to forbid any
// id which might be used as a dimension's default id.
const validateDefaultId = function (id, dimension) {
if (isInvalidDefaultId(id, dimension)) {
throw new UserError(
`The identifier "${id}" is reserved as a default value.`,
)
}
}

// For each dimension of the new result, we previously ensured that each
Expand All @@ -76,7 +88,7 @@ const validateDimensionId = function ({ dimension, id }, index, allIds) {
// not used for selection, reporting, `config.titles`, etc.
// Identifier of previous results do not need to be checked for duplicate ids
// since only their combinations matching the target result are kept.
const validateDuplicateId = function ({ dimension, id, index, allIds }) {
const validateDuplicateId = function ({ id, dimension, index, allIds }) {
const duplicateId = allIds.slice(index + 1).find((nextId) => nextId.id === id)

if (duplicateId === undefined) {
Expand Down

0 comments on commit d4faff0

Please sign in to comment.