Files
bruno/packages/bruno-tests/collection/scripting/api/res/jsonSchema.bru
sanish chirayath c4dc0bc10d feat: add JSON Schema validation support with custom chai assertion (#7301)
* feat: add JSON Schema validation support with custom chai assertion

- Introduced a new custom assertion for JSON Schema validation in chai, allowing users to validate response bodies against defined schemas.
- Updated the postman translation logic to translate `pm.response.to.have.jsonSchema` to the new assertion format.
- Enhanced tests to cover various scenarios for JSON Schema validation, ensuring accurate translations and functionality.
- Updated package dependencies to include the latest versions of relevant libraries.

* refactor: enhance JSON Schema validation assertion and add comprehensive test cases

* chore: add @rollup/plugin-json dependency and enhance JSON Schema validation tests

- Added @rollup/plugin-json as a development dependency in package.json and package-lock.json.
- Introduced new test cases for JSON Schema validation, covering various scenarios including valid schema matching, type mismatches, and required field checks.
- Updated existing assertions to utilize the new validation capabilities.

* refactor: streamline JSON Schema validation with default Ajv instance

- Updated the custom chai assertion for JSON Schema validation to utilize a default Ajv instance, improving consistency and reducing redundancy in the code.
- Enhanced the error messages in the assertion to include the actual data being validated, providing clearer feedback during validation failures.

* refactor: improve error messaging in JSON Schema validation assertion

- Enhanced the custom chai assertion for JSON Schema validation to provide clearer error messages by including a stringified version of the data being validated, improving feedback during validation failures.

* refactor: simplify Ajv instance creation in JSON Schema validation

- Removed the default Ajv instance and streamlined the creation of Ajv instances in the custom chai assertion for JSON Schema validation, ensuring consistent handling of ajvOptions across the codebase.

* feat: add support for negated JSON Schema assertions in Postman translations

- Introduced translations for `pm.response.to.not.have.jsonSchema`, `pm.response.not.to.have.jsonSchema`, and `pm.response.to.have.not.jsonSchema` to the new assertion format using `expect`.
- Enhanced the translation logic to handle these new patterns and added corresponding test cases to ensure accurate functionality.
- Updated existing tests to cover various scenarios for negated assertions, improving overall test coverage for JSON Schema validation.

* fix: improve error handling in JSON Schema validation assertions

- Added error handling for JSON schema compilation in the custom chai assertion, ensuring that any compilation errors are caught and reported with a clear message.
- Updated tests to verify that malformed schemas correctly trigger assertion errors, enhancing the robustness of JSON Schema validation.
2026-04-21 17:15:33 +05:30

350 lines
8.0 KiB
Plaintext

meta {
name: jsonSchema
type: http
seq: 9
}
post {
url: {{host}}/api/echo/json
body: json
auth: none
}
body:json {
{
"name": "John",
"age": 30,
"email": "john@example.com",
"status": "active",
"score": 95.5,
"isVerified": true,
"tags": ["developer", "tester"],
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "62701"
}
}
}
tests {
// --- Passing validations ---
test("Basic object with properties and required", function() {
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' },
email: { type: 'string' },
status: { type: 'string' },
score: { type: 'number' },
isVerified: { type: 'boolean' },
tags: { type: 'array' },
address: { type: 'object' }
},
required: ['name', 'age', 'email']
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("Nested object validation", function() {
const schema = {
type: 'object',
properties: {
address: {
type: 'object',
properties: {
street: { type: 'string' },
city: { type: 'string' },
zip: { type: 'string' }
},
required: ['street', 'city', 'zip']
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("Array items validation", function() {
const schema = {
type: 'object',
properties: {
tags: {
type: 'array',
items: { type: 'string' }
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("String pattern (regex)", function() {
const schema = {
type: 'object',
properties: {
email: {
type: 'string',
pattern: '^[^@]+@[^@]+\\.[^@]+$'
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("Enum validation", function() {
const schema = {
type: 'object',
properties: {
status: {
type: 'string',
enum: ['active', 'inactive', 'pending']
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("Number range (minimum/maximum)", function() {
const schema = {
type: 'object',
properties: {
age: {
type: 'number',
minimum: 0,
maximum: 150
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("String length constraints (minLength/maxLength)", function() {
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 100
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("allOf composition", function() {
const schema = {
allOf: [
{
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
},
{
type: 'object',
properties: { age: { type: 'number' } },
required: ['age']
}
]
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("anyOf composition", function() {
const schema = {
type: 'object',
properties: {
score: {
anyOf: [
{ type: 'number' },
{ type: 'string' }
]
}
}
};
expect(res.getBody()).to.have.jsonSchema(schema);
});
test("jsonSchema with ajvOptions", function() {
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' }
},
required: ['name', 'age']
};
expect(res.getBody()).to.have.jsonSchema(schema, { allErrors: true });
});
// --- Failure validations ---
test("Type mismatch - schema expects array, response is object", function() {
const schema = { type: 'array' };
let failed = false;
try {
expect(res.getBody()).to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
test("Missing required field", function() {
const schema = {
type: 'object',
required: ['nonExistentField']
};
let failed = false;
try {
expect(res.getBody()).to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
test("additionalProperties false rejects extra fields", function() {
const schema = {
type: 'object',
properties: {
name: { type: 'string' }
},
additionalProperties: false
};
let failed = false;
try {
expect(res.getBody()).to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
test("Enum mismatch", function() {
const schema = {
type: 'object',
properties: {
status: {
type: 'string',
enum: ['deleted', 'archived']
}
},
required: ['status']
};
let failed = false;
try {
expect(res.getBody()).to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
test("Pattern mismatch - name does not match digits-only", function() {
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
pattern: '^[0-9]+$'
}
},
required: ['name']
};
let failed = false;
try {
expect(res.getBody()).to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
// --- Malformed schema (ajv.compile error) ---
test("Malformed schema - invalid type throws assertion error", function() {
const schema = { type: 'invalidType' };
expect(() => expect(res.getBody()).to.have.jsonSchema(schema)).to.throw('JSON schema compile error');
});
// --- .not (negation) validations ---
test(".not with mismatched type - body is object, not array", function() {
const schema = { type: 'array' };
expect(res.getBody()).to.not.have.jsonSchema(schema);
});
test(".not with missing required field", function() {
const schema = {
type: 'object',
required: ['nonExistent']
};
expect(res.getBody()).to.not.have.jsonSchema(schema);
});
test(".not fails when schema actually matches", function() {
const schema = { type: 'object' };
let failed = false;
try {
expect(res.getBody()).to.not.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
// --- not.to.have (negation) validations ---
test("not.to.have with mismatched type - body is object, not array", function() {
const schema = { type: 'array' };
expect(res.getBody()).not.to.have.jsonSchema(schema);
});
test("not.to.have with missing required field", function() {
const schema = {
type: 'object',
required: ['nonExistent']
};
expect(res.getBody()).not.to.have.jsonSchema(schema);
});
test("not.to.have fails when schema actually matches", function() {
const schema = { type: 'object' };
let failed = false;
try {
expect(res.getBody()).not.to.have.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
// --- to.have.not (negation) validations ---
test("to.have.not with mismatched type - body is object, not array", function() {
const schema = { type: 'array' };
expect(res.getBody()).to.have.not.jsonSchema(schema);
});
test("to.have.not with missing required field", function() {
const schema = {
type: 'object',
required: ['nonExistent']
};
expect(res.getBody()).to.have.not.jsonSchema(schema);
});
test("to.have.not fails when schema actually matches", function() {
const schema = { type: 'object' };
let failed = false;
try {
expect(res.getBody()).to.have.not.jsonSchema(schema);
} catch (e) {
failed = true;
}
expect(failed).to.be.true;
});
}