test: inplement the cucumber conformance tests from cloudevents/spec (#238)
This commit adds cucumber-js conformance steps and includes the cucumber tests in 'npm test'. Signed-off-by: Lance Ball <lball@redhat.com>
This commit is contained in:
parent
90a998472c
commit
dca2811627
|
@ -0,0 +1,11 @@
|
|||
// cucumber.js
|
||||
let common = [
|
||||
"--require-module ts-node/register", // Load TypeScript module
|
||||
"--require test/conformance/steps.ts", // Load step definitions
|
||||
"--format progress-bar", // Load custom formatter
|
||||
"--format node_modules/cucumber-pretty", // Load custom formatter
|
||||
].join(" ");
|
||||
|
||||
module.exports = {
|
||||
default: common,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
|
@ -8,8 +8,9 @@
|
|||
"build": "tsc --project tsconfig.json && tsc --project tsconfig.browser.json && webpack",
|
||||
"lint": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}'",
|
||||
"lint:fix": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}' --fix",
|
||||
"pretest": "npm run lint",
|
||||
"test": "mocha --require ts-node/register ./test/**/*.ts",
|
||||
"pretest": "npm run lint && npm run conformance",
|
||||
"test": "mocha --require ts-node/register ./test/integration/**/*.ts",
|
||||
"conformance": "npx downtotemp https://raw.githubusercontent.com/cloudevents/conformance/master/features/http-protocol-binding.feature && cucumber-js /tmp/http-protocol-binding.feature -p default",
|
||||
"coverage": "nyc --reporter=lcov --reporter=text npm run test",
|
||||
"coverage-publish": "wget -qO - https://coverage.codacy.com/get.sh | bash -s report -l JavaScript -r coverage/lcov.info",
|
||||
"generate-docs": "typedoc --excludeNotDocumented --out docs src",
|
||||
|
@ -103,18 +104,24 @@
|
|||
"@types/ajv": "^1.0.0",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/cucumber": "^6.0.1",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^13.13.9",
|
||||
"@types/uuid": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.4.0",
|
||||
"@typescript-eslint/parser": "^3.4.0",
|
||||
"chai": "~4.2.0",
|
||||
"cucumber": "^6.0.5",
|
||||
"cucumber-pretty": "^6.0.0",
|
||||
"cucumber-tsflow": "^3.2.0",
|
||||
"downtotemp": "^0.1.2",
|
||||
"eslint": "^7.3.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"http-parser-js": "^0.5.2",
|
||||
"mocha": "~7.1.1",
|
||||
"nock": "~12.0.3",
|
||||
"nyc": "~15.0.0",
|
||||
|
|
|
@ -11,10 +11,15 @@ export * from "./date";
|
|||
export * from "./mapped";
|
||||
export * from "./pass_through";
|
||||
|
||||
const jsonParser = new JSONParser();
|
||||
const passThroughParser = new PassThroughParser();
|
||||
|
||||
export const parserByContentType: { [key: string]: Parser } = {
|
||||
[CONSTANTS.MIME_JSON]: new JSONParser(),
|
||||
[CONSTANTS.MIME_CE_JSON]: new JSONParser(),
|
||||
[CONSTANTS.MIME_OCTET_STREAM]: new PassThroughParser(),
|
||||
[CONSTANTS.MIME_JSON]: jsonParser,
|
||||
[CONSTANTS.MIME_CE_JSON]: jsonParser,
|
||||
[CONSTANTS.DEFAULT_CONTENT_TYPE]: jsonParser,
|
||||
[CONSTANTS.DEFAULT_CE_CONTENT_TYPE]: jsonParser,
|
||||
[CONSTANTS.MIME_OCTET_STREAM]: passThroughParser,
|
||||
};
|
||||
|
||||
export const parserByEncoding: { [key: string]: { [key: string]: Parser } } = {
|
||||
|
|
|
@ -62,12 +62,15 @@ export class BinaryHTTPReceiver {
|
|||
}
|
||||
|
||||
const parser = parserByContentType[eventObj.datacontenttype as string];
|
||||
if (!parser) {
|
||||
throw new ValidationError(`no parser found for content type ${eventObj.datacontenttype}`);
|
||||
}
|
||||
const parsedPayload = parser.parse(payload);
|
||||
|
||||
// Every unprocessed header can be an extension
|
||||
for (const header in sanitizedHeaders) {
|
||||
if (header.startsWith(CONSTANTS.EXTENSIONS_PREFIX)) {
|
||||
eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = sanitizedHeaders[header];
|
||||
eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = headers[header];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface Headers {
|
|||
[key: string]: string;
|
||||
}
|
||||
|
||||
export const allowedContentTypes = [CONSTANTS.MIME_JSON, CONSTANTS.MIME_OCTET_STREAM];
|
||||
export const allowedContentTypes = [CONSTANTS.DEFAULT_CONTENT_TYPE, CONSTANTS.MIME_JSON, CONSTANTS.MIME_OCTET_STREAM];
|
||||
export const requiredHeaders = [
|
||||
CONSTANTS.CE_HEADERS.ID,
|
||||
CONSTANTS.CE_HEADERS.SOURCE,
|
||||
|
@ -97,16 +97,5 @@ export function sanitize(headers: Headers): Headers {
|
|||
.filter((header) => Object.hasOwnProperty.call(headers, header))
|
||||
.forEach((header) => (sanitized[header.toLowerCase()] = headers[header]));
|
||||
|
||||
sanitized[CONSTANTS.HEADER_CONTENT_TYPE] = sanitizeContentType(sanitized[CONSTANTS.HEADER_CONTENT_TYPE]) as string;
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
function sanitizeContentType(contentType: string): string | undefined {
|
||||
if (contentType) {
|
||||
return Array.of(contentType)
|
||||
.map((c) => c.split(";"))
|
||||
.map((c) => c.shift())
|
||||
.shift();
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/* eslint-disable no-console */
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { assert } from "chai";
|
||||
import { Given, When, Then, World } from "cucumber";
|
||||
import { Receiver } from "../../src";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { HTTPParser } = require("http-parser-js");
|
||||
|
||||
const parser = new HTTPParser(HTTPParser.REQUEST);
|
||||
const receiver = new Receiver();
|
||||
|
||||
Given("HTTP Protocol Binding is supported", function (this: World) {
|
||||
return true;
|
||||
});
|
||||
|
||||
Given("an HTTP request", function (request: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const world = this;
|
||||
parser.onHeadersComplete = function (record: Record<string, []>) {
|
||||
world.headers = arrayToObject(record.headers);
|
||||
};
|
||||
parser.onBody = function (body: Buffer, offset: number) {
|
||||
world.body = body.slice(offset).toString();
|
||||
};
|
||||
parser.execute(Buffer.from(request), 0, request.length);
|
||||
return true;
|
||||
});
|
||||
|
||||
When("parsed as HTTP request", function () {
|
||||
this.cloudevent = receiver.accept(this.headers, this.body);
|
||||
return true;
|
||||
});
|
||||
|
||||
Then("the attributes are:", function (attributes: { rawTable: [] }) {
|
||||
const expected = tableToObject(attributes.rawTable);
|
||||
assert.equal(this.cloudevent.id, expected.id);
|
||||
assert.equal(this.cloudevent.type, expected.type);
|
||||
assert.equal(this.cloudevent.source, expected.source);
|
||||
assert.equal(this.cloudevent.time, new Date(expected.time).toISOString());
|
||||
assert.equal(this.cloudevent.specversion, expected.specversion);
|
||||
assert.equal(this.cloudevent.datacontenttype, expected.datacontenttype);
|
||||
return true;
|
||||
});
|
||||
|
||||
Then("the data is equal to the following JSON:", function (json: string) {
|
||||
assert.deepEqual(this.cloudevent.data, JSON.parse(json));
|
||||
return true;
|
||||
});
|
||||
|
||||
function arrayToObject(arr: []): Record<string, string> {
|
||||
const obj: Record<string, string> = {};
|
||||
// @ts-ignore
|
||||
return arr.reduce(({}, keyOrValue, index, arr) => {
|
||||
if (index % 2 === 0) {
|
||||
obj[keyOrValue] = arr[index + 1];
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
function tableToObject(table: []): Record<string, string> {
|
||||
const obj: Record<string, string> = {};
|
||||
// @ts-ignore
|
||||
return table.reduce(({}, [key, value]: Array<string, string>) => {
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
});
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { expect } from "chai";
|
||||
import { CloudEvent, Version } from "../src";
|
||||
import { CloudEventV03 } from "../src/event/v03";
|
||||
import { CloudEventV1 } from "../src/event/v1";
|
||||
import { CloudEvent, Version } from "../../src";
|
||||
import { CloudEventV03 } from "../../src/event/v03";
|
||||
import { CloudEventV1 } from "../../src/event/v1";
|
||||
|
||||
const type = "org.cncf.cloudevents.example";
|
||||
const source = "http://unit.test";
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from "chai";
|
||||
import CONSTANTS from "../src/constants";
|
||||
import CONSTANTS from "../../src/constants";
|
||||
|
||||
describe("Constants exposed by top level exports", () => {
|
||||
it("Exports an ENCODING_BASE64 constant", () => {
|
|
@ -2,8 +2,8 @@ import "mocha";
|
|||
import { expect } from "chai";
|
||||
import nock from "nock";
|
||||
|
||||
import { emitBinary, emitStructured } from "../src/transport/http";
|
||||
import { CloudEvent, Version } from "../src";
|
||||
import { emitBinary, emitStructured } from "../../src/transport/http";
|
||||
import { CloudEvent, Version } from "../../src";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
const type = "com.github.pull.create";
|
|
@ -3,9 +3,9 @@ import "mocha";
|
|||
import { expect } from "chai";
|
||||
import nock from "nock";
|
||||
|
||||
import { CloudEvent, Version } from "../src";
|
||||
import { emitBinary, emitStructured } from "../src/transport/http";
|
||||
import { asBase64 } from "../src/event/validation/is";
|
||||
import { CloudEvent, Version } from "../../src";
|
||||
import { emitBinary, emitStructured } from "../../src/transport/http";
|
||||
import { asBase64 } from "../../src/event/validation/is";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
const type = "com.github.pull.create";
|
|
@ -1,11 +1,11 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import nock from "nock";
|
||||
import CONSTANTS from "../src/constants";
|
||||
import CONSTANTS from "../../src/constants";
|
||||
|
||||
const DEFAULT_CE_CONTENT_TYPE = CONSTANTS.DEFAULT_CE_CONTENT_TYPE;
|
||||
|
||||
import { CloudEvent, Version, Emitter, Protocol, headersFor } from "../src";
|
||||
import { CloudEvent, Version, Emitter, Protocol, headersFor } from "../../src";
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
const receiver = "https://cloudevents.io/";
|
|
@ -1,7 +1,7 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import { CloudEvent, Receiver, ValidationError } from "../src";
|
||||
import { CloudEventV1 } from "../src/event/v1";
|
||||
import { CloudEvent, Receiver, ValidationError } from "../../src";
|
||||
import { CloudEventV1 } from "../../src/event/v1";
|
||||
|
||||
const receiver = new Receiver();
|
||||
const id = "1234";
|
|
@ -1,8 +1,8 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { JSONParser as Parser } from "../src/parsers/";
|
||||
import { ValidationError } from "../src/";
|
||||
import { JSONParser as Parser } from "../../src/parsers/";
|
||||
import { ValidationError } from "../../src/";
|
||||
|
||||
describe("JSON Event Format Parser", () => {
|
||||
it("Throw error when payload is an integer", () => {
|
|
@ -1,9 +1,9 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { CloudEvent, ValidationError, Version } from "../src";
|
||||
import { BinaryHTTPReceiver } from "../src/transport/http/binary_receiver";
|
||||
import CONSTANTS from "../src/constants";
|
||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||
import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver";
|
||||
import CONSTANTS from "../../src/constants";
|
||||
|
||||
const receiver = new BinaryHTTPReceiver(Version.V03);
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { CloudEvent, ValidationError, Version } from "../src";
|
||||
import { asBase64 } from "../src/event/validation";
|
||||
import { BinaryHTTPReceiver } from "../src/transport/http/binary_receiver";
|
||||
import CONSTANTS from "../src/constants";
|
||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||
import { asBase64 } from "../../src/event/validation";
|
||||
import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver";
|
||||
import CONSTANTS from "../../src/constants";
|
||||
|
||||
const receiver = new BinaryHTTPReceiver(Version.V1);
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { CloudEvent, ValidationError, Version } from "../src";
|
||||
import { StructuredHTTPReceiver } from "../src/transport/http/structured_receiver";
|
||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||
import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver";
|
||||
|
||||
const receiver = new StructuredHTTPReceiver(Version.V03);
|
||||
const type = "com.github.pull.create";
|
|
@ -1,9 +1,9 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { CloudEvent, ValidationError, Version } from "../src";
|
||||
import { asBase64 } from "../src/event/validation";
|
||||
import { StructuredHTTPReceiver } from "../src/transport/http/structured_receiver";
|
||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
||||
import { asBase64 } from "../../src/event/validation";
|
||||
import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver";
|
||||
|
||||
const receiver = new StructuredHTTPReceiver(Version.V1);
|
||||
const type = "com.github.pull.create";
|
|
@ -1,6 +1,6 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import { CloudEvent, Receiver, Emitter, Version } from "../src";
|
||||
import { CloudEvent, Receiver, Emitter, Version } from "../../src";
|
||||
|
||||
const fixture = {
|
||||
type: "org.cloudevents.test",
|
|
@ -1,7 +1,7 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import { CloudEvent, Version, ValidationError, Mode } from "../src";
|
||||
import Constants from "../src/constants";
|
||||
import { CloudEvent, Version, ValidationError, Mode } from "../../src";
|
||||
import Constants from "../../src/constants";
|
||||
|
||||
const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838";
|
||||
const type = "com.github.pull.create";
|
|
@ -1,8 +1,8 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import { CloudEvent, Version, ValidationError } from "../src";
|
||||
import { asBase64 } from "../src/event/validation";
|
||||
import Constants from "../src/constants";
|
||||
import { CloudEvent, Version, ValidationError } from "../../src";
|
||||
import { asBase64 } from "../../src/event/validation";
|
||||
import Constants from "../../src/constants";
|
||||
|
||||
const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838";
|
||||
const type = "com.github.pull.create";
|
|
@ -1,6 +1,6 @@
|
|||
import "mocha";
|
||||
import { expect } from "chai";
|
||||
import { isStringOrThrow, equalsOrThrow, isBase64, asData } from "../src/event/validation/is";
|
||||
import { isStringOrThrow, equalsOrThrow, isBase64, asData } from "../../src/event/validation/is";
|
||||
|
||||
describe("Utilities", () => {
|
||||
describe("isStringOrThrow", () => {
|
Loading…
Reference in New Issue