usebruno / bruno

Opensource IDE For Exploring and Testing Api's (lightweight alternative to postman/insomnia)
https://www.usebruno.com/
MIT License
27.83k stars 1.29k forks source link

Implement JSON Schema chai assertion #2519

Open tester-at-bmi opened 5 months ago

tester-at-bmi commented 5 months ago

I have checked the following:

Describe the feature you want to add

Would like to be able to assert JSON Schemas with chai assertion

test('Schema validation', () => {
  expect(res.body).to.have.jsonSchema(schema);
});

Mockups or Images of the feature

test('Schema validation', () => {
  expect(res.body).to.have.jsonSchema(schema);
});
image
struffel commented 2 months ago

+1 , I would absolutely love to have this!

sguerreroRGA commented 2 months ago

you can use the following in the Tests tab:

const Ajv = require('ajv'); const ajv = new Ajv();

const schema = { "definitions": { "Welcome10": { "type": "object", "additionalProperties": false, "properties": { "totalRecords": { "type": "string", "format": "integer" }, "results": { "type": "array", "items": { "$ref": "#/definitions/Result" } } }, "required": [ "results", "totalRecords" ], "title": "Welcome10" }, "Result": { "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "format": "uuid" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "completedAt": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ] }, "status": { "$ref": "#/definitions/Status" }, "clientId": { "$ref": "#/definitions/ClientID" }, "updatedBy": { "$ref": "#/definitions/AtedBy" }, "createdBy": { "$ref": "#/definitions/AtedBy" }, "productId": { "type": "string", "format": "uuid" }, "archived": { "type": "boolean" }, "name": { "type": "string" }, "columnMappings": { "type": "array", "items": { "$ref": "#/definitions/ColumnMapping" } }, "notificationEmail": { "type": "null" }, "origin": { "anyOf": [ { "$ref": "#/definitions/Origin" }, { "type": "null" } ] }, "outputTypes": { "$ref": "#/definitions/OutputTypes" }, "additionalFields": { "anyOf": [ { "type": "array", "items": { "$ref": "#/definitions/AdditionalFieldUnion" } }, { "type": "null" } ] } }, "required": [ "additionalFields", "archived", "clientId", "columnMappings", "completedAt", "createdAt", "createdBy", "id", "name", "notificationEmail", "origin", "outputTypes", "productId", "status", "updatedAt", "updatedBy" ], "title": "Result" }, "AdditionalFieldClass": { "type": "object", "additionalProperties": false, "properties": { "value": { "$ref": "#/definitions/ValueUnion" }, "inputName": { "$ref": "#/definitions/InputName" } }, "required": [ "inputName", "value" ], "title": "AdditionalFieldClass" }, "ColumnMapping": { "type": "object", "additionalProperties": false, "properties": { "mapTo": { "$ref": "#/definitions/MapToEnum" }, "inputName": { "$ref": "#/definitions/MapToEnum" } }, "required": [ "inputName", "mapTo" ], "title": "ColumnMapping" }, "AdditionalFieldUnion": { "anyOf": [ { "type": "array", "items": { "$ref": "#/definitions/AdditionalFieldClass" } }, { "$ref": "#/definitions/AdditionalFieldClass" } ], "title": "AdditionalFieldUnion" }, "ValueUnion": { "anyOf": [ { "$ref": "#/definitions/ValueEnum" }, { "type": "string", "format": "integer" } ], "title": "ValueUnion" }, "InputName": { "type": "string", "enum": [ "censusType", "stopLoss", "specDeductible" ], "title": "InputName" }, "ValueEnum": { "type": "string", "enum": [ "employee", "Yes", "member", "No" ], "title": "ValueEnum" }, "ClientID": { "type": "string", "enum": [ "rgax" ], "title": "ClientID" }, "MapToEnum": { "type": "string", "enum": [ "RECORD_ID", "PERSON_FIRST_NAME", "_blank", "PERSON_LAST_NAME", "GENDER", "ZIP", "DOB", "groupid", "fname", "mname", "lname", "gender", "zip", "dob", "coverage", "PERSON_MIDDLE_INITIAL", "streetnumber", "streetname", "streettype", "city", "state", "coveragelife", "coveragesupplife", "coveragedisability", "coverage_LTD", "suffix", "coverage_suppLife", "coveragelife.", "coveragesupplife.", "coveragedisability.", "coverageVoluntary LTD", "coverageSTD", "coverageVoluntarySTD", "coveragelife. ", "coveragesupplife. ", "coveragedisability. ", "birthdate", "coverage-supplife", "coveragevoluntarylongtermdisability", "coverageshorttermdisability", "coveragevoluntaryshorttermdisability" ], "title": "MapToEnum" }, "AtedBy": { "type": "string", "enum": [ "s0052117@rgare.net", "s0052216@rgare.net", "s0048924@rgare.net", "s0048496@rgare.net", "s0050997@rgare.net", "s0045255@rgare.net", "s0050372@rgare.net", "s0052047@rgare.net", "s0044905@rgare.net", "Job-Workflow" ], "title": "AtedBy" }, "Origin": { "type": "string", "enum": [ "api" ], "title": "Origin" }, "OutputTypes": { "type": "string", "enum": [ "csv,pdf", "csv" ], "title": "OutputTypes" }, "Status": { "type": "string", "enum": [ "COMPLETE", "ERROR", "NEW", "PROCESSING" ], "title": "Status" } } };

test("Verify JSON returns the right schema", function() { const data = res.getBody(); const valid = ajv.validate(schema, data); //expect(res.getStatus()).to.equal(200); expect(valid).to.be.true; if (!valid) console.log(ajv.errors); });

struffel commented 2 months ago

Oh, cool! I will try this out.

struffel commented 1 month ago

Okay, I tried it and one issue I ran into is that I have the JSON schema in a separate json file on disk and it apparently does not have the permission to access it (or I am doing something wrong). So for a simple schema that can be embedded this works but for a more advanced schema that is already "delivered" in the form of several files on disk it's not quite there.