Initial public release of Apophis — invariant-driven automated API testing
This commit is contained in:
@@ -30,7 +30,6 @@ export interface Config {
|
||||
environments?: Record<string, EnvironmentPolicy>;
|
||||
profiles?: Record<string, ProfileDefinition>;
|
||||
presets?: Record<string, PresetDefinition>;
|
||||
generationProfiles?: Record<string, 'quick' | 'standard' | 'thorough' | { base: 'quick' | 'standard' | 'thorough' }>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -107,11 +106,6 @@ const CONFIG_SCHEMA: Record<string, SchemaField> = {
|
||||
optional: true,
|
||||
properties: {},
|
||||
},
|
||||
generationProfiles: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
properties: {},
|
||||
},
|
||||
packs: {
|
||||
type: 'array',
|
||||
optional: true,
|
||||
@@ -151,7 +145,6 @@ const PROFILE_SCHEMA: Record<string, SchemaField> = {
|
||||
// Schema for PresetDefinition values (inside presets.<name>)
|
||||
const PRESET_SCHEMA: Record<string, SchemaField> = {
|
||||
name: { type: 'string', optional: false },
|
||||
depth: { type: 'string', optional: true, enumValues: ['quick', 'standard', 'deep'] },
|
||||
timeout: { type: 'number', optional: true, min: 0 },
|
||||
parallel: { type: 'boolean', optional: true },
|
||||
chaos: { type: 'boolean', optional: true },
|
||||
@@ -160,12 +153,9 @@ const PRESET_SCHEMA: Record<string, SchemaField> = {
|
||||
sampling: { type: 'number', optional: true },
|
||||
blocking: { type: 'boolean', optional: true },
|
||||
sinks: { type: 'object', optional: true },
|
||||
runs: { type: 'number', optional: true, min: 1 },
|
||||
};
|
||||
|
||||
const GENERATION_PROFILE_ALIAS_SCHEMA: Record<string, SchemaField> = {
|
||||
base: { type: 'string', optional: false, enumValues: ['quick', 'standard', 'thorough'] },
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Config discovery
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -259,7 +249,6 @@ function getDynamicContainerSchema(path: string): Record<string, SchemaField> |
|
||||
if (path === 'profiles') return PROFILE_SCHEMA;
|
||||
if (path === 'presets') return PRESET_SCHEMA;
|
||||
if (path === 'environments') return ENVIRONMENT_POLICY_SCHEMA;
|
||||
if (path === 'generationProfiles') return GENERATION_PROFILE_ALIAS_SCHEMA;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -267,7 +256,7 @@ function getDynamicContainerSchema(path: string): Record<string, SchemaField> |
|
||||
* Check if a path is inside a dynamic container (e.g., profiles.foo, presets.bar).
|
||||
*/
|
||||
function isInsideDynamicContainer(path: string): boolean {
|
||||
return path.startsWith('profiles.') || path.startsWith('presets.') || path.startsWith('environments.') || path.startsWith('generationProfiles.');
|
||||
return path.startsWith('profiles.') || path.startsWith('presets.') || path.startsWith('environments.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,18 +368,11 @@ export function validateConfigAgainstSchema(
|
||||
|
||||
// Handle dynamic containers: profiles, presets, environments
|
||||
// The keys are user-defined names; their values have specific schemas
|
||||
const isDynamicContainer = path === 'profiles' || path === 'presets' || path === 'environments' || path === 'generationProfiles';
|
||||
const isDynamicContainer = path === 'profiles' || path === 'presets' || path === 'environments';
|
||||
if (!fieldSchema && isDynamicContainer) {
|
||||
const childSchema = getDynamicContainerSchema(path);
|
||||
const fieldValue = obj[key];
|
||||
if (path === 'generationProfiles' && typeof fieldValue === 'string') {
|
||||
validateType(
|
||||
fieldValue,
|
||||
{ type: 'string', optional: false, enumValues: ['quick', 'standard', 'thorough'] },
|
||||
currentPath,
|
||||
key,
|
||||
);
|
||||
} else if (childSchema && fieldValue !== null && typeof fieldValue === 'object') {
|
||||
if (childSchema && fieldValue !== null && typeof fieldValue === 'object') {
|
||||
// Validate the dynamic container value against its specific schema
|
||||
validateConfigAgainstSchema(fieldValue, childSchema, currentPath);
|
||||
} else if (childSchema) {
|
||||
@@ -633,19 +615,6 @@ export function validateConfigSemantics(config: Config): void {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (preset.depth !== undefined) {
|
||||
const validDepths = ['quick', 'standard', 'deep'];
|
||||
const depthValue = preset.depth;
|
||||
if (typeof depthValue === 'string' && !validDepths.includes(depthValue as string)) {
|
||||
throw new ConfigValidationError(
|
||||
`Preset "${presetName}" has invalid depth: "${depthValue}"`,
|
||||
`presets.${presetName}.depth`,
|
||||
'depth',
|
||||
depthValue,
|
||||
`Must be one of: ${validDepths.join(', ')}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user