Skip to content

Commit 2029b46

Browse files
committed
test: convert all tests to typescript
Signed-off-by: Lance Ball <[email protected]>
1 parent ff3b791 commit 2029b46

30 files changed

+1836
-1887
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
"prelint": "npm run build",
1010
"lint": "standardx src examples",
1111
"fix": "standardx --fix",
12-
"pretest": "npm run lint && npm run test:ts",
13-
"test": "echo 'skipping js tests for now' # mocha test/**/*.js",
14-
"test:ts": "mocha --require ts-node/register ./test-ts/**/*.ts",
12+
"pretest": "npm run lint",
13+
"test": "mocha --require ts-node/register ./test/**/*.ts",
1514
"coverage": "nyc --reporter=lcov --reporter=text npm run test",
1615
"coverage-publish": "wget -qO - https://coverage.codacy.com/get.sh | bash -s report -l JavaScript -r coverage/lcov.info",
1716
"generate-docs": "typedoc --excludeNotDocumented --out docs src",

src/event/cloudevent.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ValidationError } from "./validation";
66

77
export const enum Version { V1 = "1.0", V03 = "0.3" };
88

9-
109
/**
1110
* A CloudEvent describes event data in common formats to provide
1211
* interoperability across services, platforms and systems.
@@ -20,7 +19,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
2019
datacontenttype?: string;
2120
dataschema?: string; // TODO: Is a URI type needed?
2221
subject?: string;
23-
time?: string|Date;
22+
#_time?: string|Date;
2423
data?: any;
2524
data_base64?: any;
2625
[key: string]: any; // Extensions should not exist as it's own object, but instead be keyed directly from the event
@@ -54,7 +53,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
5453
this.subject = properties.subject;
5554
delete properties.subject;
5655

57-
this.time = properties.time;
56+
this.#_time = properties.time;
5857
delete properties.time;
5958

6059
this.data = properties.data;
@@ -77,10 +76,10 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
7776
delete properties.schemaurl;
7877

7978
// Make sure time has a default value and whatever is provided is formatted
80-
if (!this.time) {
81-
this.time = new Date().toISOString();
82-
} else if (this.time instanceof Date) {
83-
this.time = this.time.toISOString();
79+
if (!this.#_time) {
80+
this.#_time = new Date().toISOString();
81+
} else if (this.#_time instanceof Date) {
82+
this.#_time = this.#_time.toISOString();
8483
}
8584

8685
// sanity checking
@@ -96,6 +95,20 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
9695
}
9796
}
9897

98+
get time() {
99+
return this.#_time as string|Date;
100+
}
101+
102+
set time(val: string|Date) {
103+
this.#_time = new Date(val).toISOString();
104+
}
105+
106+
toJSON(): object {
107+
const event = { ...this };
108+
event.time = this.time;
109+
return event;
110+
}
111+
99112
/**
100113
* Validates this CloudEvent against the schema
101114
* @returns boolean
@@ -109,7 +122,6 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
109122
} else {
110123
throw new ValidationError("invalid payload");
111124
}
112-
return false;
113125
}
114126

115127
public static create(attributes: CloudEventV1Attributes | CloudEventV03Attributes, version?: Version): CloudEvent {

src/transport/http/binary_receiver.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Headers, validate } from "./headers";
55
import { binaryParsers as v1Parsers } from "./v1/parsers";
66
import { binaryParsers as v03Parsers } from "./v03/parsers";
77
import { parserByContentType, MappedParser } from "../../parsers";
8-
import { isString, isBase64 } from "../../event/validation";
8+
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
99
import CONSTANTS from "../../constants";
1010

1111
/**
@@ -33,6 +33,16 @@ export class BinaryHTTPReceiver {
3333
* @throws {ValidationError} of the event does not conform to the spec
3434
*/
3535
parse(payload: any, headers: Headers): CloudEvent {
36+
if (!payload) throw new ValidationError("payload is null or undefined");
37+
if (!headers) throw new ValidationError("headers is null or undefined");
38+
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
39+
40+
if (headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] &&
41+
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V03 &&
42+
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V1) {
43+
throw new ValidationError(`invalid spec version ${headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]}`);
44+
}
45+
3646
payload = isString(payload) && isBase64(payload)
3747
? Buffer.from(payload, "base64").toString()
3848
: payload;

src/transport/http/headers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ export function headersFor(event: CloudEvent): Headers {
6262
}
6363
}
6464
});
65+
// Treat time specially, since it's handled with getters and setters in CloudEvent
66+
if (event.time) {
67+
headers[CONSTANTS.CE_HEADERS.TIME] = event.time as string;
68+
}
6569
return headers;
6670
}
6771

src/transport/http/structured_emitter.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ export function emitStructured(event: CloudEvent, options: Options): Promise<obj
2121
}
2222

2323
function format(event: CloudEvent): string {
24-
const formatted = JSON.parse(JSON.stringify(event));
25-
return JSON.stringify(formatted, (key: string, value: any) => {
26-
if (key === CONSTANTS.CE_ATTRIBUTES.DATA && isBinary(value)) {
27-
return asBase64(value);
28-
}
29-
return value;
30-
});
24+
if (isBinary(event.data)) {
25+
event.data_base64 = asBase64(event.data);
26+
}
27+
const json = JSON.stringify(event);
28+
delete event.data_base64;
29+
return json;
3130
}

src/transport/http/structured_receiver.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { CloudEvent, Version } from "../..";
22
import { Headers, validate, sanitize } from "./headers";
3-
import { Parser } from "../../parsers";
3+
import { Parser, JSONParser } from "../../parsers";
44
import { parserByContentType } from "../../parsers";
55
import { structuredParsers as v1Parsers } from "./v1/parsers";
66
import { structuredParsers as v03Parsers } from "./v03/parsers";
7-
import { isString, isBase64 } from "../../event/validation";
7+
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow, isBinary, asBase64 } from "../../event/validation";
88
import { CloudEventV1, validateV1 as validateV1 } from "../../event/v1";
99
import { CloudEventV03, validateV03 as validateV03 } from "../../event/v03";
1010
import CONSTANTS from "../../constants";
@@ -33,18 +33,36 @@ export class StructuredHTTPReceiver {
3333
* @throws {ValidationError} if the payload and header combination do not conform to the spec
3434
*/
3535
parse(payload: any, headers: Headers) {
36+
if (!payload) throw new ValidationError("payload is null or undefined");
37+
if (!headers) throw new ValidationError("headers is null or undefined");
38+
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
39+
40+
if (headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] &&
41+
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V03 &&
42+
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V1) {
43+
throw new ValidationError(`invalid spec version ${headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]}`);
44+
}
45+
3646
payload = isString(payload) && isBase64(payload)
3747
? Buffer.from(payload, "base64").toString()
3848
: payload;
3949

40-
// Clone and low case all headers names
50+
// Clone and low case all headers names
4151
const sanitizedHeaders = sanitize(headers);
4252

43-
const parser: Parser = parserByContentType[sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]];
53+
const contentType = sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE];
54+
const parser: Parser = contentType ? parserByContentType[contentType] : new JSONParser();
55+
if (!parser) throw new ValidationError(`invalid content type ${sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]}`);
4456
const incoming = parser.parse(payload);
4557

4658
const eventObj: { [key: string]: any } = {};
4759
const parserMap = this.version === Version.V1 ? v1Parsers : v03Parsers;
60+
61+
// if the incoming data is in binary form, encode to base64
62+
if (isBinary(incoming.data)) {
63+
eventObj.data_base64 = asBase64(incoming.data);
64+
}
65+
4866
parserMap.forEach((value, key) => {
4967
if (incoming[key]) {
5068
eventObj[value.name] = value.parser.parse(incoming[key]);
@@ -55,6 +73,13 @@ export class StructuredHTTPReceiver {
5573
eventObj[key] = incoming[key];
5674
}
5775

76+
// ensure data content is correctly encoded
77+
if (eventObj.data && eventObj.datacontentencoding) {
78+
if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 && !isBase64(eventObj.data)) {
79+
throw new ValidationError("invalid payload");
80+
}
81+
}
82+
5883
const cloudevent = CloudEvent.create(eventObj as CloudEventV1 | CloudEventV03);
5984

6085
// Validates the event

0 commit comments

Comments
 (0)