diff --git a/lib/bindings/http/http_receiver.js b/lib/bindings/http/http_receiver.js index 2e30fca..2ffd79f 100644 --- a/lib/bindings/http/http_receiver.js +++ b/lib/bindings/http/http_receiver.js @@ -2,6 +2,7 @@ const V03Binary = require("./receiver_binary_0_3.js"); const V03Structured = require("./receiver_structured_0_3.js"); const V1Binary = require("./receiver_binary_1.js"); const V1Structured = require("./receiver_structured_1.js"); +const ValidationError = require("../../validation_error.js"); const { SPEC_V03, SPEC_V1, @@ -63,7 +64,7 @@ function getMode(headers) { if (headers[BINARY_HEADERS_1.ID]) { return "binary"; } - throw new TypeError("no cloud event detected"); + throw new ValidationError("no cloud event detected"); } function getVersion(mode, headers, body) { diff --git a/lib/bindings/http/receiver_binary.js b/lib/bindings/http/receiver_binary.js index a7acafe..82087a1 100644 --- a/lib/bindings/http/receiver_binary.js +++ b/lib/bindings/http/receiver_binary.js @@ -2,6 +2,7 @@ const { HEADER_CONTENT_TYPE, MIME_JSON, DEFAULT_SPEC_VERSION_HEADER } = require("./constants.js"); const Commons = require("./commons.js"); const CloudEvent = require("../../cloudevent.js"); +const ValidationError = require("../../validation_error.js"); const { isDefinedOrThrow, @@ -10,15 +11,12 @@ const { function validateArgs(payload, attributes) { Array.of(payload) - .filter((p) => isDefinedOrThrow(p, - { message: "payload is null or undefined" })) - .filter((p) => isStringOrObjectOrThrow(p, - { message: "payload must be an object or a string" })) + .filter((p) => isDefinedOrThrow(p, new ValidationError("payload is null or undefined"))) + .filter((p) => isStringOrObjectOrThrow(p, new ValidationError("payload must be an object or a string"))) .shift(); Array.of(attributes) - .filter((a) => isDefinedOrThrow(a, - { message: "attributes is null or undefined" })) + .filter((a) => isDefinedOrThrow(a, new ValidationError("attributes is null or undefined"))) .shift(); } @@ -58,22 +56,18 @@ BinaryHTTPReceiver.prototype.check = function(payload, headers) { const contentTypeHeader = sanityHeaders[HEADER_CONTENT_TYPE]; const noContentType = !this.allowedContentTypes.includes(contentTypeHeader); if (contentTypeHeader && noContentType) { - const err = new TypeError("invalid content type"); - err.errors = [sanityHeaders[HEADER_CONTENT_TYPE]]; - throw err; + throw new ValidationError("invalid content type", [sanityHeaders[HEADER_CONTENT_TYPE]]); } this.requiredHeaders .filter((required) => !sanityHeaders[required]) .forEach((required) => { - throw new TypeError(`header '${required}' not found`); + throw new ValidationError(`header '${required}' not found`); }); if (sanityHeaders[DEFAULT_SPEC_VERSION_HEADER] !== this.specversion) { - const err = new TypeError("invalid spec version"); - err.errors = [sanityHeaders[DEFAULT_SPEC_VERSION_HEADER]]; - throw err; + throw new ValidationError("invalid spec version", [sanityHeaders[DEFAULT_SPEC_VERSION_HEADER]]); } // No erros! Its contains the minimum required attributes diff --git a/lib/bindings/http/receiver_binary_0_3.js b/lib/bindings/http/receiver_binary_0_3.js index 3f76329..7a38e84 100644 --- a/lib/bindings/http/receiver_binary_0_3.js +++ b/lib/bindings/http/receiver_binary_0_3.js @@ -1,5 +1,6 @@ const Constants = require("./constants.js"); const Spec = require("../../specs/spec_0_3.js"); +const ValidationError = require("../../validation_error.js"); const JSONParser = require("../../formats/json/parser.js"); const Base64Parser = require("../../formats/base64.js"); @@ -85,9 +86,7 @@ function checkDecorator(payload, headers) { .filter((header) => !allowedEncodings.includes(headers[header])) .forEach((header) => { // TODO: using forEach here seems off - const err = new TypeError("unsupported datacontentencoding"); - err.errors = [headers[header]]; - throw err; + throw new ValidationError("unsupported datacontentencoding"); }); } diff --git a/lib/bindings/http/receiver_structured.js b/lib/bindings/http/receiver_structured.js index 638d62b..f16f4aa 100644 --- a/lib/bindings/http/receiver_structured.js +++ b/lib/bindings/http/receiver_structured.js @@ -1,6 +1,7 @@ const Constants = require("./constants.js"); const Commons = require("./commons.js"); const CloudEvent = require("../../cloudevent.js"); +const ValidationError = require("../../validation_error.js"); const { isDefinedOrThrow, @@ -9,15 +10,12 @@ const { function validateArgs(payload, attributes) { Array.of(payload) - .filter((p) => isDefinedOrThrow(p, - { message: "payload is null or undefined" })) - .filter((p) => isStringOrObjectOrThrow(p, - { message: "payload must be an object or string" })) + .filter((p) => isDefinedOrThrow(p, new ValidationError("payload is null or undefined"))) + .filter((p) => isStringOrObjectOrThrow(p, new ValidationError("payload must be an object or string"))) .shift(); Array.of(attributes) - .filter((a) => isDefinedOrThrow(a, - { message: "attributes is null or undefined" })) + .filter((a) => isDefinedOrThrow(a, new ValidationError("attributes is null or undefined"))) .shift(); } @@ -41,9 +39,7 @@ StructuredHTTPReceiver.prototype.check = function(payload, headers) { // Validation Level 1 if (!this.allowedContentTypes .includes(sanityHeaders[Constants.HEADER_CONTENT_TYPE])) { - const err = new TypeError("invalid content type"); - err.errors = [sanityHeaders[Constants.HEADER_CONTENT_TYPE]]; - throw err; + throw new ValidationError("invalid content type", [sanityHeaders[Constants.HEADER_CONTENT_TYPE]]); } // No erros! Its contains the minimum required attributes diff --git a/lib/bindings/http/unmarshaller.js b/lib/bindings/http/unmarshaller.js index db79a8d..dd071df 100644 --- a/lib/bindings/http/unmarshaller.js +++ b/lib/bindings/http/unmarshaller.js @@ -6,6 +6,7 @@ const { MIME_OCTET_STREAM } = require("./constants.js"); const Commons = require("./commons.js"); +const ValidationError = require("../../validation_error.js"); const STRUCTURED = "structured"; const BINARY = "binary"; @@ -29,18 +30,18 @@ function resolveBindingName(payload, headers) { if (allowedStructuredContentTypes.includes(contentType)) { return STRUCTURED; } - throwTypeError("structured+type not allowed", contentType); + throwValidationError("structured+type not allowed", contentType); } else { // Binary if (allowedBinaryContentTypes.includes(contentType)) { return BINARY; } - throwTypeError("content type not allowed", contentType); + throwValidationError("content type not allowed", contentType); } } -function throwTypeError(msg, contentType) { - const err = new TypeError(msg); +function throwValidationError(msg, contentType) { + const err = new ValidationError(msg); err.errors = [contentType]; throw err; } @@ -52,16 +53,16 @@ class Unmarshaller { unmarshall(payload, headers) { if (!payload) { - throw new TypeError("payload is null or undefined"); + throw new ValidationError("payload is null or undefined"); } if (!headers) { - throw new TypeError("headers is null or undefined"); + throw new ValidationError("headers is null or undefined"); } // Validation level 1 const sanityHeaders = Commons.sanityAndClone(headers); if (!sanityHeaders[HEADER_CONTENT_TYPE]) { - throw new TypeError("content-type header not found"); + throw new ValidationError("content-type header not found"); } // Resolve the binding diff --git a/lib/cloudevent.js b/lib/cloudevent.js index 83ab4be..99a2c61 100644 --- a/lib/cloudevent.js +++ b/lib/cloudevent.js @@ -7,12 +7,12 @@ const Formatter = require("./formats/json/formatter.js"); class CloudEvent { /** * Creates a new CloudEvent instance - * @param {Spec} [UserSpec] A CloudEvent version specification - * @param {Formatter} [UserFormatter] Converts the event into a readable string + * @param {Spec} [userSpec] A CloudEvent version specification + * @param {Formatter} [userFormatter] Converts the event into a readable string */ - constructor(UserSpec, UserFormatter) { - this.spec = (UserSpec) ? new UserSpec(CloudEvent) : new Spec(CloudEvent); - this.formatter = (UserFormatter) ? new UserFormatter() : new Formatter(); + constructor(userSpec, userFormatter) { + this.spec = userSpec ? new userSpec(CloudEvent) : new Spec(CloudEvent); + this.formatter = userFormatter ? new userFormatter() : new Formatter(); // The map of extensions this.extensions = {}; diff --git a/lib/formats/json/parser.js b/lib/formats/json/parser.js index 0d83357..12efdac 100644 --- a/lib/formats/json/parser.js +++ b/lib/formats/json/parser.js @@ -3,11 +3,10 @@ const { isDefinedOrThrow, isStringOrObjectOrThrow } = require("../../utils/fun.js"); +const ValidationError = require("../../validation_error.js"); -const invalidPayloadTypeError = - new Error("invalid payload type, allowed are: string or object"); -const nullOrUndefinedPayload = - new Error("null or undefined payload"); +const invalidPayloadTypeError = new ValidationError("invalid payload type, allowed are: string or object"); +const nullOrUndefinedPayload = new ValidationError("null or undefined payload"); const asJSON = (v) => (isString(v) ? JSON.parse(v) : v); diff --git a/lib/specs/spec_0_3.js b/lib/specs/spec_0_3.js index f07e0fb..323aa04 100644 --- a/lib/specs/spec_0_3.js +++ b/lib/specs/spec_0_3.js @@ -1,5 +1,6 @@ const { v4: uuidv4 } = require("uuid"); const Ajv = require("ajv"); +const ValidationError = require("../validation_error.js"); const { isBase64, @@ -168,9 +169,7 @@ Spec03.prototype.check = function(ce) { const toCheck = (!ce ? this.payload : ce); if (!isValidAgainstSchema(toCheck)) { - const err = new TypeError("invalid payload"); - err.errors = isValidAgainstSchema.errors; - throw err; + throw new ValidationError("invalid payload", [isValidAgainstSchema.errors]); } Array.of(toCheck) @@ -178,11 +177,7 @@ Spec03.prototype.check = function(ce) { .map((tc) => tc.datacontentencoding.toLocaleLowerCase("en-US")) .filter((dce) => !Object.keys(SUPPORTED_CONTENT_ENCODING).includes(dce)) .forEach((dce) => { - const err = new TypeError("invalid payload"); - err.errors = [ - `Unsupported content encoding: ${dce}` - ]; - throw err; + throw new ValidationError("invalid payload", [`Unsupported content encoding: ${dce}`]); }); Array.of(toCheck) @@ -200,11 +195,7 @@ Spec03.prototype.check = function(ce) { .filter((tc) => !SUPPORTED_CONTENT_ENCODING[tc.datacontentencoding] .check(tc.data)) .forEach((tc) => { - const err = new TypeError("invalid payload"); - err.errors = [ - `Invalid content encoding of data: ${tc.data}` - ]; - throw err; + throw new ValidationError("invalid payload", [`Invalid content encoding of data: ${tc.data}`]); }); }; @@ -304,7 +295,7 @@ Spec03.prototype.addExtension = function(key, value) { if (!Object.prototype.hasOwnProperty.call(RESERVED_ATTRIBUTES, key)) { this.payload[key] = value; } else { - throw new TypeError(`Reserved attribute name: '${key}'`); + throw new ValidationError(`Reserved attribute name: '${key}'`); } return this; }; diff --git a/lib/specs/spec_1.js b/lib/specs/spec_1.js index 6415d15..7f2d628 100644 --- a/lib/specs/spec_1.js +++ b/lib/specs/spec_1.js @@ -1,5 +1,6 @@ const { v4: uuidv4 } = require("uuid"); const Ajv = require("ajv"); +const ValidationError = require("../validation_error.js"); const { asData, @@ -191,9 +192,7 @@ Spec1.prototype.check = function(ce) { const toCheck = (!ce ? this.payload : ce); if (!isValidAgainstSchema(toCheck)) { - const err = new TypeError("invalid payload"); - err.errors = isValidAgainstSchema.errors; - throw err; + throw new ValidationError("invalid payload", [isValidAgainstSchema.errors]); } }; @@ -284,10 +283,10 @@ Spec1.prototype.addExtension = function(key, value) { if (isValidType(value)) { this.payload[key] = value; } else { - throw new TypeError("Invalid type of extension value"); + throw new ValidationError("Invalid type of extension value"); } } else { - throw new TypeError(`Reserved attribute name: '${key}'`); + throw new ValidationError(`Reserved attribute name: '${key}'`); } return this; }; diff --git a/lib/validation_error.js b/lib/validation_error.js new file mode 100644 index 0000000..08f4fa9 --- /dev/null +++ b/lib/validation_error.js @@ -0,0 +1,18 @@ +/** + * A Error class that will be thrown when a CloudEvent + * cannot be properly validated against a the specification. + */ +class ValidationError extends TypeError { + /** + * Constructs a new {ValidationError} with the message + * and array of additional errors. + * @param {string} message the error message + * @param {[string]|[ErrorObject]} [errors] any additional errors related to validation + */ + constructor(message, errors) { + super(message); + this.errors = errors ? errors : []; + } +} + +module.exports = ValidationError; \ No newline at end of file diff --git a/test/bindings/http/promiscuous_receiver_test.js b/test/bindings/http/promiscuous_receiver_test.js index 17ab81c..1a942f9 100644 --- a/test/bindings/http/promiscuous_receiver_test.js +++ b/test/bindings/http/promiscuous_receiver_test.js @@ -8,6 +8,7 @@ const { BINARY_HEADERS_03, BINARY_HEADERS_1 } = require("../../../lib/bindings/http/constants.js"); +const ValidationError = require("../../../lib/validation_error.js"); const receiver = new HTTPReceiver(); const id = "1234"; @@ -30,7 +31,7 @@ describe("HTTP Transport Binding Receiver for CloudEvents", () => { }; expect(receiver.accept.bind(receiver, {}, payload)) - .to.throw("no cloud event detected"); + .to.throw(ValidationError, "no cloud event detected"); }); }); diff --git a/test/bindings/http/receiver_binary_0_3_tests.js b/test/bindings/http/receiver_binary_0_3_tests.js index cbddc1d..51df0db 100644 --- a/test/bindings/http/receiver_binary_0_3_tests.js +++ b/test/bindings/http/receiver_binary_0_3_tests.js @@ -1,7 +1,7 @@ const expect = require("chai").expect; -const HTTPBinaryReceiver = - require("../../../lib/bindings/http/receiver_binary_0_3.js"); +const HTTPBinaryReceiver = require("../../../lib/bindings/http/receiver_binary_0_3.js"); +const ValidationError = require("../../../lib/validation_error.js"); const { BINARY_HEADERS_03, SPEC_V03, @@ -19,7 +19,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload is null or undefined"); + .to.throw(ValidationError, "payload is null or undefined"); }); it("Throw error when attributes arg is null or undefined", () => { @@ -29,7 +29,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("attributes is null or undefined"); + .to.throw(ValidationError, "attributes is null or undefined"); }); it("Throw error when payload is not an object or string", () => { @@ -39,7 +39,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload must be an object or a string"); + .to.throw(ValidationError, "payload must be an object or a string"); }); it("Throw error when headers has no 'ce-type'", () => { @@ -54,7 +54,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-type' not found"); + .to.throw(ValidationError, "header 'ce-type' not found"); }); it("Throw error when headers has no 'ce-specversion'", () => { @@ -69,7 +69,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-specversion' not found"); + .to.throw(ValidationError, "header 'ce-specversion' not found"); }); it("Throw error when headers has no 'ce-source'", () => { @@ -84,7 +84,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-source' not found"); + .to.throw(ValidationError, "header 'ce-source' not found"); }); it("Throw error when headers has no 'ce-id'", () => { @@ -99,7 +99,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-id' not found"); + .to.throw(ValidationError, "header 'ce-id' not found"); }); it("Throw error when spec is not 0.3", () => { @@ -115,7 +115,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.parse.bind(receiver, payload, attributes)) - .to.throw("invalid spec version"); + .to.throw(ValidationError, "invalid spec version"); }); it("Throw error when the content-type is invalid", () => { @@ -131,7 +131,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("No error when all required headers are in place", () => { diff --git a/test/bindings/http/receiver_binary_1_tests.js b/test/bindings/http/receiver_binary_1_tests.js index 4be6900..b43e729 100644 --- a/test/bindings/http/receiver_binary_1_tests.js +++ b/test/bindings/http/receiver_binary_1_tests.js @@ -5,6 +5,7 @@ const { SPEC_V1, HEADER_CONTENT_TYPE } = require("../../../lib/bindings/http/constants.js"); +const ValidationError = require("../../../lib/validation_error.js"); const HTTPBinaryReceiver = require("../../../lib/bindings/http/receiver_binary_1.js"); @@ -20,7 +21,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload is null or undefined"); + .to.throw(ValidationError, "payload is null or undefined"); }); it("Throw error when attributes arg is null or undefined", () => { @@ -30,7 +31,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("attributes is null or undefined"); + .to.throw(ValidationError, "attributes is null or undefined"); }); it("Throw error when payload is not an object or string", () => { @@ -40,7 +41,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload must be an object or a string"); + .to.throw(ValidationError, "payload must be an object or a string"); }); it("Throw error when headers has no 'ce-type'", () => { @@ -55,7 +56,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-type' not found"); + .to.throw(ValidationError, "header 'ce-type' not found"); }); it("Throw error when headers has no 'ce-specversion'", () => { @@ -70,7 +71,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-specversion' not found"); + .to.throw(ValidationError, "header 'ce-specversion' not found"); }); it("Throw error when headers has no 'ce-source'", () => { @@ -85,7 +86,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-source' not found"); + .to.throw(ValidationError, "header 'ce-source' not found"); }); it("Throw error when headers has no 'ce-id'", () => { @@ -100,7 +101,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("header 'ce-id' not found"); + .to.throw(ValidationError, "header 'ce-id' not found"); }); it("Throw error when spec is not 1.0", () => { @@ -116,7 +117,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.parse.bind(receiver, payload, attributes)) - .to.throw("invalid spec version"); + .to.throw(ValidationError, "invalid spec version"); }); it("Throw error when the content-type is invalid", () => { @@ -132,7 +133,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("No error when content-type is unspecified", () => { @@ -186,8 +187,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getType()) - .to.equal("type"); + expect(actual.getType()).to.equal("type"); }); it("CloudEvent contains 'specversion'", () => { @@ -209,8 +209,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getSpecversion()) - .to.equal(SPEC_V1); + expect(actual.getSpecversion()).to.equal(SPEC_V1); }); it("CloudEvent contains 'source'", () => { @@ -232,8 +231,8 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getSource()) - .to.equal("/source"); + expect(actual.getSource()).to.equal("/source"); + }); it("CloudEvent contains 'id'", () => { @@ -255,8 +254,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getId()) - .to.equal("id"); + expect(actual.getId()).to.equal("id"); }); it("CloudEvent contains 'time'", () => { @@ -278,8 +276,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getTime()) - .to.equal("2019-06-16T11:42:00.000Z"); + expect(actual.getTime()).to.equal("2019-06-16T11:42:00.000Z"); }); it("CloudEvent contains 'dataschema'", () => { @@ -301,8 +298,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getDataschema()) - .to.equal("http://schema.registry/v1"); + expect(actual.getDataschema()).to.equal("http://schema.registry/v1"); }); it("CloudEvent contains 'contenttype' (application/json)", () => { @@ -324,8 +320,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getDataContentType()) - .to.equal("application/json"); + expect(actual.getDataContentType()).to.equal("application/json"); }); it("CloudEvent contains 'contenttype' (application/octet-stream)", () => { @@ -345,8 +340,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getDataContentType()) - .to.equal("application/octet-stream"); + expect(actual.getDataContentType()).to.equal("application/octet-stream"); }); it("CloudEvent contains 'data' (application/json)", () => { @@ -368,8 +362,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getData()) - .to.deep.equal(payload); + expect(actual.getData()).to.deep.equal(payload); }); it("CloudEvent contains 'data' (application/octet-stream)", () => { @@ -389,8 +382,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getData()) - .to.deep.equal(payload); + expect(actual.getData()).to.deep.equal(payload); }); it("The content of 'data' is base64 for binary", () => { @@ -417,8 +409,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual.getData()) - .to.deep.equal(expected); + expect(actual.getData()).to.deep.equal(expected); }); it("No error when all attributes are in place", () => { @@ -440,11 +431,9 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actual = receiver.parse(payload, attributes); // assert - expect(actual) - .to.be.an("object"); + expect(actual).to.be.an("object"); - expect(actual) - .to.have.property("format"); + expect(actual).to.have.property("format"); }); it("Should accept 'extension1'", () => { @@ -469,8 +458,7 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => { const actualExtensions = actual.getExtensions(); // assert - expect(actualExtensions.extension1) - .to.equal(extension1); + expect(actualExtensions.extension1).to.equal(extension1); }); }); }); diff --git a/test/bindings/http/receiver_structured_0_3_test.js b/test/bindings/http/receiver_structured_0_3_test.js index a374240..59fa54b 100644 --- a/test/bindings/http/receiver_structured_0_3_test.js +++ b/test/bindings/http/receiver_structured_0_3_test.js @@ -1,8 +1,7 @@ const expect = require("chai").expect; const v03 = require("../../../v03/index.js"); - -const HTTPStructuredReceiver = - require("../../../lib/bindings/http/receiver_structured_0_3.js"); +const ValidationError = require("../../../lib/validation_error.js"); +const HTTPStructuredReceiver = require("../../../lib/bindings/http/receiver_structured_0_3.js"); const receiver = new HTTPStructuredReceiver(); @@ -31,7 +30,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload is null or undefined"); + .to.throw(ValidationError, "payload is null or undefined"); }); it("Throw error when attributes arg is null or undefined", () => { @@ -41,7 +40,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("attributes is null or undefined"); + .to.throw(ValidationError, "attributes is null or undefined"); }); it("Throw error when payload is not an object or string", () => { @@ -51,7 +50,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload must be an object or string"); + .to.throw(ValidationError, "payload must be an object or string"); }); it("Throw error when the content-type is invalid", () => { @@ -63,7 +62,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("Throw error data content encoding is base64, but 'data' is not", @@ -87,7 +86,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.parse.bind(receiver, payload, attributes)) - .to.throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); }); it("No error when all required stuff are in place", () => { @@ -135,7 +134,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { // act and assert expect(receiver.parse.bind(receiver, payload, headers)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("Should accept event that follows the spec", () => { diff --git a/test/bindings/http/receiver_structured_1_test.js b/test/bindings/http/receiver_structured_1_test.js index 2a5e9ee..6436c25 100644 --- a/test/bindings/http/receiver_structured_1_test.js +++ b/test/bindings/http/receiver_structured_1_test.js @@ -1,11 +1,9 @@ const expect = require("chai").expect; const v1 = require("../../../v1/index.js"); const { CloudEvent } = require("../../../index.js"); - const { asBase64 } = require("../../../lib/utils/fun.js"); - -const HTTPStructuredReceiver = - require("../../../lib/bindings/http/receiver_structured_1.js"); +const ValidationError = require("../../../lib/validation_error.js"); +const HTTPStructuredReceiver = require("../../../lib/bindings/http/receiver_structured_1.js"); const receiver = new HTTPStructuredReceiver(); @@ -30,7 +28,7 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload is null or undefined"); + .to.throw(ValidationError, "payload is null or undefined"); }); it("Throw error when attributes arg is null or undefined", () => { @@ -40,7 +38,7 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("attributes is null or undefined"); + .to.throw(ValidationError, "attributes is null or undefined"); }); it("Throw error when payload is not an object or string", () => { @@ -50,7 +48,7 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("payload must be an object or string"); + .to.throw(ValidationError, "payload must be an object or string"); }); it("Throw error when the content-type is invalid", () => { @@ -62,7 +60,7 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", // act and assert expect(receiver.check.bind(receiver, payload, attributes)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("No error when all required stuff are in place", () => { @@ -95,7 +93,7 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", // act and assert expect(receiver.parse.bind(receiver, payload, headers)) - .to.throw("invalid content type"); + .to.throw(ValidationError, "invalid content type"); }); it("Should accept event that follows the spec", () => { diff --git a/test/bindings/http/unmarshaller_0_3_tests.js b/test/bindings/http/unmarshaller_0_3_tests.js index bee22b9..55ff87e 100644 --- a/test/bindings/http/unmarshaller_0_3_tests.js +++ b/test/bindings/http/unmarshaller_0_3_tests.js @@ -1,4 +1,5 @@ const expect = require("chai").expect; +const ValidationError = require("../../../lib/validation_error.js"); const Unmarshaller = require("../../../lib/bindings/http/unmarshaller_0_3.js"); const { CloudEvent } = require("../../../index.js"); const v03 = require("../../../v03/index.js"); @@ -23,18 +24,18 @@ const un = new Unmarshaller(); describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { it("Throw error when payload is null", () => { - expect(() => un.unmarshall(null)).to.throw("payload is null or undefined"); + expect(() => un.unmarshall(null)).to.throw(ValidationError, "payload is null or undefined"); }); it("Throw error when headers is null", () => { - expect(() => un.unmarshall({})).to.throw("headers is null or undefined"); + expect(() => un.unmarshall({})).to.throw(ValidationError, "headers is null or undefined"); expect(() => un.unmarshall({}, null)).to - .throw("headers is null or undefined"); + .throw(ValidationError, "headers is null or undefined"); }); it("Throw error when there is no content-type header", () => { expect(() => un.unmarshall({}, {})).to - .throw("content-type header not found"); + .throw(ValidationError, "content-type header not found"); }); it("Throw error when content-type is not allowed", () => { @@ -42,7 +43,7 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { "content-type": "text/xml" }; expect(() => un.unmarshall({}, headers)).to - .throw("content type not allowed"); + .throw(ValidationError, "content type not allowed"); }); describe("Structured", () => { @@ -54,7 +55,7 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { // act and assert expect(() => un.unmarshall({}, headers)).to - .throw("structured+type not allowed"); + .throw(ValidationError, "structured+type not allowed"); }); it("Throw error when the event does not follow the spec 0.3", () => { @@ -67,11 +68,10 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { "content-type": "application/cloudevents+json" }; - expect(() => un.unmarshall(payload, headers)).to - .throw(TypeError); + expect(() => un.unmarshall(payload, headers)).to.throw(ValidationError); }); - it("Should accept event that follow the spec 0.3", () => { + it("Should accept event TypeErrorthat follow the spec 0.3", () => { const payload = new CloudEvent(v03.Spec) .type(type) @@ -129,7 +129,7 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { }; expect(() => un.unmarshall(payload, attributes)).to - .throw("content type not allowed"); + .throw(ValidationError, "content type not allowed"); }); it("Throw error when the event does not follow the spec 0.3", () => { @@ -148,7 +148,7 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { }; expect(() => un.unmarshall(payload, attributes)).to - .throw("header 'ce-specversion' not found"); + .throw(ValidationError, "header 'ce-specversion' not found"); }); it("No error when all attributes are in place", () => { @@ -186,7 +186,7 @@ describe("HTTP Transport Binding Unmarshaller for CloudEvents v0.3", () => { }; expect(() => un.unmarshall(payload, attributes)).to - .throw("unsupported datacontentencoding"); + .throw(ValidationError, "unsupported datacontentencoding"); }); it("No error when 'ce-datacontentencoding' is base64", () => { diff --git a/test/formats/json/parser_test.js b/test/formats/json/parser_test.js index 5cb2cff..e0a7dbe 100644 --- a/test/formats/json/parser_test.js +++ b/test/formats/json/parser_test.js @@ -1,5 +1,6 @@ const expect = require("chai").expect; const Parser = require("../../../lib/formats/json/parser.js"); +const ValidationError = require("../../../lib/validation_error.js"); describe("JSON Event Format Parser", () => { it("Throw error when payload is an integer", () => { @@ -9,7 +10,7 @@ describe("JSON Event Format Parser", () => { // act and assert expect(parser.parse.bind(parser, payload)) - .to.throw("invalid payload type, allowed are: string or object"); + .to.throw(ValidationError, "invalid payload type, allowed are: string or object"); }); it("Throw error when payload is null", () => { @@ -18,8 +19,7 @@ describe("JSON Event Format Parser", () => { const parser = new Parser(); // act and assert - expect(parser.parse.bind(parser, payload)) - .to.throw("null or undefined payload"); + expect(parser.parse.bind(parser, payload)).to.throw(ValidationError, "null or undefined payload"); }); it("Throw error when payload is undefined", () => { @@ -27,8 +27,7 @@ describe("JSON Event Format Parser", () => { const parser = new Parser(); // act and assert - expect(parser.parse.bind(parser)) - .to.throw("null or undefined payload"); + expect(parser.parse.bind(parser)).to.throw(ValidationError, "null or undefined payload"); }); it("Throw error when payload is a float", () => { @@ -38,7 +37,7 @@ describe("JSON Event Format Parser", () => { // act and assert expect(parser.parse.bind(parser, payload)) - .to.throw("invalid payload type, allowed are: string or object"); + .to.throw(ValidationError, "invalid payload type, allowed are: string or object"); }); it("Throw error when payload is an invalid JSON", () => { @@ -46,9 +45,8 @@ describe("JSON Event Format Parser", () => { const payload = "gg"; const parser = new Parser(); - // act and assert - expect(parser.parse.bind(parser, payload)) - .to.throw("Unexpected token g in JSON at position 0"); + // TODO: Should the parser catch the SyntaxError and re-throw a ValidationError? + expect(parser.parse.bind(parser, payload)).to.throw(SyntaxError, "Unexpected token g in JSON at position 0"); }); it("Must accept when the payload is a string well formed as JSON", () => { @@ -60,7 +58,6 @@ describe("JSON Event Format Parser", () => { const actual = parser.parse(payload); // assert - expect(actual) - .to.be.an("object"); + expect(actual).to.be.an("object"); }); }); diff --git a/test/spec_0_3_tests.js b/test/spec_0_3_tests.js index 8db95ff..4de79d6 100644 --- a/test/spec_0_3_tests.js +++ b/test/spec_0_3_tests.js @@ -6,6 +6,7 @@ const { ENCODING_BASE64, SPEC_V03 } = require("../lib/bindings/http/constants.js"); +const ValidationError = require("../lib/validation_error.js"); const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838"; const type = "com.github.pull.create"; @@ -83,7 +84,7 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when use a reserved name as extension", () => { expect(cloudevent.addExtension.bind(cloudevent, "id")) - .to.throw("Reserved attribute name: 'id'"); + .to.throw(ValidationError, "Reserved attribute name: 'id'"); }); }); @@ -92,16 +93,14 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.id; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.id = id; }); it("should throw an erro when is empty", () => { cloudevent.spec.payload.id = ""; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.id = id; }); }); @@ -110,8 +109,7 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.source; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.source = source; }); }); @@ -120,16 +118,14 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.specversion; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.specversion = SPEC_V03; }); it("should throw an error when is empty", () => { cloudevent.spec.payload.specversion = ""; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.specversion = SPEC_V03; }); }); @@ -138,16 +134,14 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.type; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.type = type; }); it("should throw an error when is an empty string", () => { cloudevent.type(""); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.type(type); }); @@ -163,8 +157,7 @@ describe("CloudEvents Spec v0.3", () => { .data("Y2xvdWRldmVudHMK") .dataContentEncoding("binary"); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); delete cloudevent.spec.payload.datacontentencoding; cloudevent.data(data); }); @@ -177,8 +170,7 @@ describe("CloudEvents Spec v0.3", () => { .dataContentType("text/plain"); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); delete cloudevent.spec.payload.datacontentencoding; cloudevent.data(data); @@ -216,8 +208,7 @@ describe("CloudEvents Spec v0.3", () => { it("should throw an error when is an empty string", () => { cloudevent.subject(""); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.subject(type); }); }); diff --git a/test/spec_1_tests.js b/test/spec_1_tests.js index af211f2..23f5aee 100644 --- a/test/spec_1_tests.js +++ b/test/spec_1_tests.js @@ -3,6 +3,7 @@ const Spec1 = require("../lib/specs/spec_1.js"); const { CloudEvent } = require("../index.js"); const { v4: uuidv4 } = require("uuid"); const { asBase64 } = require("../lib/utils/fun.js"); +const ValidationError = require("../lib/validation_error.js"); const id = uuidv4(); const type = "com.github.pull.create"; @@ -106,13 +107,13 @@ describe("CloudEvents Spec v1.0", () => { it("should throw an error when use a reserved name as extension", () => { expect(cloudevent.addExtension.bind(cloudevent, "id")) - .to.throw("Reserved attribute name: 'id'"); + .to.throw(ValidationError, "Reserved attribute name: 'id'"); }); it("should throw an error when use an invalid type", () => { expect(cloudevent .addExtension.bind(cloudevent, "invalid-val", { cool: "nice" })) - .to.throw("Invalid type of extension value"); + .to.throw(ValidationError, "Invalid type of extension value"); }); }); @@ -122,15 +123,14 @@ describe("CloudEvents Spec v1.0", () => { delete cloudevent.spec.payload.id; expect(cloudevent.format.bind(cloudevent)) .to - .throw("invalid payload"); + .throw(ValidationError, "invalid payload"); cloudevent.spec.payload.id = id; }); it("should throw an error when is empty", () => { cloudevent.spec.payload.id = ""; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.id = id; }); }); @@ -139,8 +139,7 @@ describe("CloudEvents Spec v1.0", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.source; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.source = source; }); }); @@ -149,16 +148,14 @@ describe("CloudEvents Spec v1.0", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.specversion; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.specversion = "1.0"; }); it("should throw an error when is empty", () => { cloudevent.spec.payload.specversion = ""; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.specversion = "1.0"; }); }); @@ -167,16 +164,14 @@ describe("CloudEvents Spec v1.0", () => { it("should throw an error when is absent", () => { delete cloudevent.spec.payload.type; expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.spec.payload.type = type; }); it("should throw an error when is an empty string", () => { cloudevent.type(""); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.type(type); }); @@ -190,8 +185,7 @@ describe("CloudEvents Spec v1.0", () => { it("should throw an error when is an empty string", () => { cloudevent.subject(""); expect(cloudevent.format.bind(cloudevent)) - .to - .throw("invalid payload"); + .to.throw(ValidationError, "invalid payload"); cloudevent.subject(type); }); });