Add VerifyLogger to JS tests (#2472)

This commit is contained in:
BrennanConroy 2018-06-12 12:49:15 -07:00 committed by GitHub
parent 7317762b29
commit cb8264321d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1672 additions and 1425 deletions

View File

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
import { EOL } from "os";
import { ILogger, LogLevel } from "../src/ILogger";
import { HttpTransportType } from "../src/ITransport";
export function eachTransport(action: (transport: HttpTransportType) => void) {
@ -21,3 +23,38 @@ export function eachEndpointUrl(action: (givenUrl: string, expectedUrl: string)
urls.forEach((t) => action(t[0], t[1]));
}
type ErrorMatchFunction = (error: string) => boolean;
export class VerifyLogger implements ILogger {
public unexpectedErrors: string[];
private expectedErrors: ErrorMatchFunction[];
public constructor(...expectedErrors: Array<RegExp | string | ErrorMatchFunction>) {
this.unexpectedErrors = [];
this.expectedErrors = [];
expectedErrors.forEach((element) => {
if (element instanceof RegExp) {
this.expectedErrors.push((e) => element.test(e));
} else if (typeof element === "string") {
this.expectedErrors.push((e) => element === e);
} else {
this.expectedErrors.push(element);
}
}, this);
}
public static async run(fn: (logger: VerifyLogger) => Promise<void>, ...expectedErrors: Array<RegExp | string | ErrorMatchFunction>): Promise<void> {
const logger = new VerifyLogger(...expectedErrors);
await fn(logger);
expect(logger.unexpectedErrors.join(EOL)).toBe("");
}
public log(logLevel: LogLevel, message: string): void {
if (logLevel >= LogLevel.Error) {
if (!this.expectedErrors.some((fn) => fn(message))) {
this.unexpectedErrors.push(message);
}
}
}
}

View File

@ -8,9 +8,9 @@ import { HttpTransportType, ITransport, TransferFormat } from "../src/ITransport
import { HttpError } from "../src/Errors";
import { NullLogger } from "../src/Loggers";
import { EventSourceConstructor } from "../src/Polyfills";
import { EventSourceConstructor, WebSocketConstructor } from "../src/Polyfills";
import { eachEndpointUrl, eachTransport } from "./Common";
import { eachEndpointUrl, eachTransport, VerifyLogger } from "./Common";
import { TestHttpClient } from "./TestHttpClient";
import { PromiseSource } from "./Utils";
@ -42,11 +42,13 @@ describe("HttpConnection", () => {
});
it("starting connection fails if getting id fails", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => Promise.reject("error"))
.on("GET", () => ""),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -54,9 +56,13 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("error");
},
"Failed to start the connection: error",
"Failed to complete negotiation with the server: error");
});
it("cannot start a running connection", async () => {
await VerifyLogger.run(async (logger) => {
const negotiating = new PromiseSource();
const options: IHttpConnectionOptions = {
...commonOptions,
@ -65,6 +71,7 @@ describe("HttpConnection", () => {
negotiating.resolve();
return defaultNegotiateResponse;
}),
logger,
transport: {
connect() {
return Promise.resolve();
@ -91,8 +98,10 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
it("can start a stopped connection", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
@ -100,6 +109,7 @@ describe("HttpConnection", () => {
return Promise.reject("reached negotiate");
})
.on("GET", () => ""),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -111,9 +121,13 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("reached negotiate");
},
"Failed to complete negotiation with the server: reached negotiate",
"Failed to start the connection: reached negotiate");
});
it("can stop a starting connection", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
@ -125,24 +139,30 @@ describe("HttpConnection", () => {
await connection.stop();
return "";
}),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
await connection.start(TransferFormat.Text);
});
});
it("can stop a non-started connection", async () => {
const connection = new HttpConnection("http://tempuri.org", commonOptions);
await VerifyLogger.run(async (logger) => {
const connection = new HttpConnection("http://tempuri.org", { ...commonOptions, logger });
await connection.stop();
});
});
it("start throws after all transports fail", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
.on("GET", () => { throw new Error("fail"); }),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org?q=myData", options);
@ -150,9 +170,12 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Unable to initialize any of the available transports.");
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
it("preserves user's query string", async () => {
await VerifyLogger.run(async (logger) => {
const connectUrl = new PromiseSource<string>();
const fakeTransport: ITransport = {
connect(url: string): Promise<void> {
@ -174,6 +197,7 @@ describe("HttpConnection", () => {
httpClient: new TestHttpClient()
.on("POST", () => "{ \"connectionId\": \"42\" }")
.on("GET", () => ""),
logger,
transport: fakeTransport,
} as IHttpConnectionOptions;
@ -188,9 +212,11 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
eachEndpointUrl((givenUrl: string, expectedUrl: string) => {
it(`negotiate request for '${givenUrl}' puts 'negotiate' at the end of the path`, async () => {
await VerifyLogger.run(async (logger) => {
const negotiateUrl = new PromiseSource<string>();
const options: IHttpConnectionOptions = {
...commonOptions,
@ -203,6 +229,7 @@ describe("HttpConnection", () => {
return new HttpResponse(204);
})
.on("DELETE", () => new HttpResponse(202)),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection(givenUrl, options);
@ -215,11 +242,15 @@ describe("HttpConnection", () => {
} finally {
await connection.stop();
}
},
"Failed to complete negotiation with the server: Error: We don't care how this turns out",
"Failed to start the connection: Error: We don't care how this turns out");
});
});
eachTransport((requestedTransport: HttpTransportType) => {
it(`cannot be started if requested ${HttpTransportType[requestedTransport]} transport not available on server`, async () => {
await VerifyLogger.run(async (logger) => {
// Clone the default response
const negotiateResponse = { ...defaultNegotiateResponse };
@ -232,6 +263,7 @@ describe("HttpConnection", () => {
httpClient: new TestHttpClient()
.on("POST", () => negotiateResponse)
.on("GET", () => new HttpResponse(204)),
logger,
transport: requestedTransport,
} as IHttpConnectionOptions;
@ -240,34 +272,12 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Unable to initialize any of the available transports.");
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
for (const [val, name] of [[null, "null"], [undefined, "undefined"], [0, "0"]]) {
it(`can be started using ${HttpTransportType[requestedTransport]} transport when transport mask is ${name}`, async () => {
const negotiateResponse = {
availableTransports: [
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
],
connectionId: "abc123",
};
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => negotiateResponse)
.on("GET", () => new HttpResponse(204)),
transport: val,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
await connection.start(TransferFormat.Text);
});
}
it(`cannot be started if server's only transport (${HttpTransportType[requestedTransport]}) is masked out by the transport option`, async () => {
await VerifyLogger.run(async (logger) => {
const negotiateResponse = {
availableTransports: [
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
@ -289,6 +299,7 @@ describe("HttpConnection", () => {
httpClient: new TestHttpClient()
.on("POST", () => negotiateResponse)
.on("GET", () => new HttpResponse(204)),
logger,
transport: transportMask,
} as IHttpConnectionOptions;
@ -300,27 +311,57 @@ describe("HttpConnection", () => {
} catch (e) {
expect(e.message).toBe("Unable to initialize any of the available transports.");
}
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
});
for (const [val, name] of [[null, "null"], [undefined, "undefined"], [0, "0"]]) {
it(`can be started when transport mask is ${name}`, async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => defaultNegotiateResponse)
.on("GET", () => new HttpResponse(200))
.on("DELETE", () => new HttpResponse(202)),
logger,
transport: val,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
await connection.start(TransferFormat.Text);
await connection.stop();
});
});
}
it("cannot be started if no transport available on server and no transport requested", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
.on("GET", () => ""),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Unable to initialize any of the available transports.");
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
it("does not send negotiate request if WebSockets transport requested explicitly and skipNegotiation is true", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient(),
logger,
skipNegotiation: true,
transport: HttpTransportType.WebSockets,
} as IHttpConnectionOptions;
@ -329,12 +370,16 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("'WebSocket' is not supported in your environment.");
},
"Failed to start the connection: Error: 'WebSocket' is not supported in your environment.");
});
it("does not start non WebSockets transport requested explicitly and skipNegotiation is true", async () => {
it("does not start non WebSockets transport if requested explicitly and skipNegotiation is true", async () => {
await VerifyLogger.run(async (logger) => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient(),
logger,
skipNegotiation: true,
transport: HttpTransportType.LongPolling,
} as IHttpConnectionOptions;
@ -343,9 +388,12 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Negotiation can only be skipped when using the WebSocket transport directly.");
},
"Failed to start the connection: Error: Negotiation can only be skipped when using the WebSocket transport directly.");
});
it("redirects to url when negotiate returns it", async () => {
await VerifyLogger.run(async (logger) => {
let firstNegotiate = true;
let firstPoll = true;
const httpClient = new TestHttpClient()
@ -371,6 +419,7 @@ describe("HttpConnection", () => {
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient,
logger,
transport: HttpTransportType.LongPolling,
} as IHttpConnectionOptions;
@ -387,14 +436,17 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
it("fails to start if negotiate redirects more than 100 times", async () => {
await VerifyLogger.run(async (logger) => {
const httpClient = new TestHttpClient()
.on("POST", /negotiate$/, () => ({ url: "https://another.domain.url/chat" }));
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient,
logger,
transport: HttpTransportType.LongPolling,
} as IHttpConnectionOptions;
@ -402,9 +454,12 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Negotiate redirection limit exceeded.");
},
"Failed to start the connection: Error: Negotiate redirection limit exceeded.");
});
it("redirects to url when negotiate returns it with access token", async () => {
await VerifyLogger.run(async (logger) => {
let firstNegotiate = true;
let firstPoll = true;
const httpClient = new TestHttpClient()
@ -445,6 +500,7 @@ describe("HttpConnection", () => {
...commonOptions,
accessTokenFactory: () => "firstSecret",
httpClient,
logger,
transport: HttpTransportType.LongPolling,
} as IHttpConnectionOptions;
@ -461,8 +517,10 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
it("authorization header removed when token factory returns null and using LongPolling", async () => {
await VerifyLogger.run(async (logger) => {
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
let httpClientGetCount = 0;
@ -495,10 +553,10 @@ describe("HttpConnection", () => {
if (authorizationValue) {
fail("Second long poll request should have no authorization header.");
}
throw new Error("fail");
}
})
.on("DELETE", () => new HttpResponse(202)),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -511,8 +569,10 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
it("sets inherentKeepAlive feature when using LongPolling", async () => {
await VerifyLogger.run(async (logger) => {
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
let httpClientGetCount = 0;
@ -525,11 +585,10 @@ describe("HttpConnection", () => {
if (httpClientGetCount === 1) {
// First long polling request must succeed so start completes
return "";
} else {
throw new Error("fail");
}
})
.on("DELETE", () => new HttpResponse(202)),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -540,14 +599,17 @@ describe("HttpConnection", () => {
await connection.stop();
}
});
});
it("does not select ServerSentEvents transport when not available in environment", async () => {
await VerifyLogger.run(async (logger) => {
const serverSentEventsTransport = { transport: "ServerSentEvents", transferFormats: ["Text"] };
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => ({ connectionId: "42", availableTransports: [serverSentEventsTransport] })),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -555,15 +617,19 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Unable to initialize any of the available transports.");
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
it("does not select WebSockets transport when not available in environment", async () => {
await VerifyLogger.run(async (logger) => {
const webSocketsTransport = { transport: "WebSockets", transferFormats: ["Text"] };
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", () => ({ connectionId: "42", availableTransports: [webSocketsTransport] })),
logger,
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
@ -571,6 +637,8 @@ describe("HttpConnection", () => {
await expect(connection.start(TransferFormat.Text))
.rejects
.toThrow("Unable to initialize any of the available transports.");
},
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
describe(".constructor", () => {
@ -580,6 +648,7 @@ describe("HttpConnection", () => {
});
it("uses global WebSocket if defined", async () => {
await VerifyLogger.run(async (logger) => {
// tslint:disable-next-line:no-string-literal
global["WebSocket"] = class WebSocket {
constructor() {
@ -589,6 +658,7 @@ describe("HttpConnection", () => {
const options: IHttpConnectionOptions = {
...commonOptions,
logger,
skipNegotiation: true,
transport: HttpTransportType.WebSockets,
} as IHttpConnectionOptions;
@ -601,9 +671,12 @@ describe("HttpConnection", () => {
// tslint:disable-next-line:no-string-literal
delete global["WebSocket"];
},
"Failed to start the connection: Error: WebSocket constructor called.");
});
it("uses global EventSource if defined", async () => {
await VerifyLogger.run(async (logger) => {
let eventSourceConstructorCalled: boolean = false;
// tslint:disable-next-line:no-string-literal
global["EventSource"] = class EventSource {
@ -623,6 +696,7 @@ describe("HttpConnection", () => {
connectionId: defaultConnectionId,
};
}),
logger,
transport: HttpTransportType.ServerSentEvents,
} as IHttpConnectionOptions;
@ -636,9 +710,13 @@ describe("HttpConnection", () => {
// tslint:disable-next-line:no-string-literal
delete global["EventSource"];
},
"Failed to start the transport 'ServerSentEvents': Error: EventSource constructor called.",
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
it("uses EventSource constructor from options if provided", async () => {
await VerifyLogger.run(async (logger) => {
let eventSourceConstructorCalled: boolean = false;
class TestEventSource {
@ -661,6 +739,7 @@ describe("HttpConnection", () => {
connectionId: defaultConnectionId,
};
}),
logger,
transport: HttpTransportType.ServerSentEvents,
} as IHttpConnectionOptions;
@ -671,9 +750,13 @@ describe("HttpConnection", () => {
.toThrow("Unable to initialize any of the available transports.");
expect(eventSourceConstructorCalled).toEqual(true);
},
"Failed to start the transport 'ServerSentEvents': Error: EventSource constructor called.",
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
});
it("uses WebSocket constructor from options if provided", async () => {
await VerifyLogger.run(async (logger) => {
class TestWebSocket {
// The "_" prefix tell TypeScript not to worry about unused parameter, but tslint doesn't like it.
// tslint:disable-next-line:variable-name
@ -684,7 +767,8 @@ describe("HttpConnection", () => {
const options: IHttpConnectionOptions = {
...commonOptions,
WebSocket: TestWebSocket,
WebSocket: TestWebSocket as WebSocketConstructor,
logger,
skipNegotiation: true,
transport: HttpTransportType.WebSockets,
} as IHttpConnectionOptions;
@ -694,15 +778,19 @@ describe("HttpConnection", () => {
await expect(connection.start())
.rejects
.toThrow("WebSocket constructor called.");
},
"Failed to start the connection: Error: WebSocket constructor called.");
});
});
describe("startAsync", () => {
it("throws if an unsupported TransferFormat is provided", async () => {
await VerifyLogger.run(async (logger) => {
// Force TypeScript to let us call start incorrectly
const connection: any = new HttpConnection("http://tempuri.org", commonOptions);
const connection: any = new HttpConnection("http://tempuri.org", { ...commonOptions, logger });
expect(() => connection.start(42)).toThrowError("Unknown transferFormat value: 42.");
});
});
});
});

View File

@ -11,6 +11,7 @@ import { NullLogger } from "../src/Loggers";
import { IStreamSubscriber } from "../src/Stream";
import { TextMessageFormat } from "../src/TextMessageFormat";
import { VerifyLogger } from "./Common";
import { delay, PromiseSource } from "./Utils";
function createHubConnection(connection: IConnection, logger?: ILogger | null, protocol?: IHubProtocol | null) {
@ -21,8 +22,9 @@ describe("HubConnection", () => {
describe("start", () => {
it("sends negotiation message", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
await hubConnection.start();
expect(connection.sentData.length).toBe(1);
@ -34,10 +36,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("state connected", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
expect(hubConnection.state).toBe(HubConnectionState.Disconnected);
try {
await hubConnection.start();
@ -47,11 +51,13 @@ describe("HubConnection", () => {
}
});
});
});
describe("ping", () => {
it("automatically sends multiple pings", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
hubConnection.keepAliveIntervalInMilliseconds = 5;
@ -66,11 +72,13 @@ describe("HubConnection", () => {
}
});
});
});
describe("stop", () => {
it("state disconnected", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
expect(hubConnection.state).toBe(HubConnectionState.Disconnected);
try {
await hubConnection.start();
@ -81,12 +89,14 @@ describe("HubConnection", () => {
}
});
});
});
describe("send", () => {
it("sends a non blocking invocation", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
// We don't actually care to wait for the send.
// tslint:disable-next-line:no-floating-promises
@ -109,12 +119,14 @@ describe("HubConnection", () => {
}
});
});
});
describe("invoke", () => {
it("sends an invocation", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
// We don't actually care to wait for the send.
// tslint:disable-next-line:no-floating-promises
@ -138,8 +150,10 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can process handshake from text", async () => {
await VerifyLogger.run(async (logger) => {
let protocolCalled = false;
const mockProtocol = new TestProtocol(TransferFormat.Text);
@ -148,7 +162,7 @@ describe("HubConnection", () => {
};
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, null, mockProtocol);
const hubConnection = createHubConnection(connection, logger, mockProtocol);
try {
const data = "{}" + TextMessageFormat.RecordSeparator;
@ -160,8 +174,10 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can process handshake from binary", async () => {
await VerifyLogger.run(async (logger) => {
let protocolCalled = false;
const mockProtocol = new TestProtocol(TransferFormat.Binary);
@ -170,7 +186,7 @@ describe("HubConnection", () => {
};
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, null, mockProtocol);
const hubConnection = createHubConnection(connection, logger, mockProtocol);
try {
// handshake response + message separator
const data = [0x7b, 0x7d, 0x1e];
@ -183,15 +199,17 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can process handshake and additional messages from binary", async () => {
await VerifyLogger.run(async (logger) => {
let receivedProcotolData: ArrayBuffer | undefined;
const mockProtocol = new TestProtocol(TransferFormat.Binary);
mockProtocol.onreceive = (d) => receivedProcotolData = d as ArrayBuffer;
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, null, mockProtocol);
const hubConnection = createHubConnection(connection, logger, mockProtocol);
try {
// handshake response + message separator + message pack message
const data = [
@ -211,15 +229,17 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can process handshake and additional messages from text", async () => {
await VerifyLogger.run(async (logger) => {
let receivedProcotolData: string | undefined;
const mockProtocol = new TestProtocol(TransferFormat.Text);
mockProtocol.onreceive = (d) => receivedProcotolData = d as string;
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, null, mockProtocol);
const hubConnection = createHubConnection(connection, logger, mockProtocol);
try {
const data = "{}" + TextMessageFormat.RecordSeparator + "{\"type\":6}" + TextMessageFormat.RecordSeparator;
@ -230,10 +250,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("rejects the promise when an error is received", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -246,10 +268,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("resolves the promise when a result is received", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -262,11 +286,13 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("completes pending invocations when stopped", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
connection.receiveHandshakeResponse();
@ -275,11 +301,13 @@ describe("HubConnection", () => {
await expect(invokePromise).rejects.toThrow("Invocation canceled due to connection being closed.");
});
});
it("completes pending invocations when connection is lost", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -293,19 +321,22 @@ describe("HubConnection", () => {
}
});
});
});
describe("on", () => {
it("invocations ignored in callbacks not registered", async () => {
await VerifyLogger.run(async (logger) => {
const warnings: string[] = [];
const logger = {
const wrappingLogger = {
log: (logLevel: LogLevel, message: string) => {
if (logLevel === LogLevel.Warning) {
warnings.push(message);
}
logger.log(logLevel, message);
},
} as ILogger;
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, logger);
const hubConnection = createHubConnection(connection, wrappingLogger);
try {
connection.receiveHandshakeResponse();
@ -321,18 +352,21 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("invocations ignored in callbacks that have registered then unregistered", async () => {
await VerifyLogger.run(async (logger) => {
const warnings: string[] = [];
const logger = {
const wrappingLogger = {
log: (logLevel: LogLevel, message: string) => {
if (logLevel === LogLevel.Warning) {
warnings.push(message);
}
logger.log(logLevel, message);
},
} as ILogger;
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, logger);
const hubConnection = createHubConnection(connection, wrappingLogger);
try {
connection.receiveHandshakeResponse();
@ -353,10 +387,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("all handlers can be unregistered with just the method name", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -368,7 +404,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "inc",
type: MessageType.Invocation,
@ -378,7 +413,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "inc",
type: MessageType.Invocation,
@ -389,10 +423,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("a single handler can be unregistered with the method name and handler", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -404,7 +440,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "inc",
type: MessageType.Invocation,
@ -414,7 +449,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "inc",
type: MessageType.Invocation,
@ -425,10 +459,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can't register the same handler multiple times", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -439,7 +475,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "inc",
type: MessageType.Invocation,
@ -450,10 +485,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("callback invoked when servers invokes a method on the client", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -462,7 +499,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: ["test"],
invocationId: "0",
nonblocking: true,
target: "message",
type: MessageType.Invocation,
@ -473,10 +509,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("stop on handshake error", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let closeError: Error | undefined;
hubConnection.onclose((e) => closeError = e);
@ -487,11 +525,14 @@ describe("HubConnection", () => {
} finally {
await hubConnection.stop();
}
},
"Server returned handshake error: Error!");
});
it("stop on close message", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let isClosed = false;
let closeError: Error | undefined;
@ -512,10 +553,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("stop on error close message", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let isClosed = false;
let closeError: Error | undefined;
@ -537,10 +580,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can have multiple callbacks", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -551,7 +596,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "message",
type: MessageType.Invocation,
@ -563,10 +607,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can unsubscribe from on", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -576,7 +622,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "message",
type: MessageType.Invocation,
@ -586,7 +631,6 @@ describe("HubConnection", () => {
connection.receive({
arguments: [],
invocationId: "0",
nonblocking: true,
target: "message",
type: MessageType.Invocation,
@ -597,10 +641,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("unsubscribing from non-existing callbacks no-ops", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.off("_", () => { });
hubConnection.on("message", (t) => { });
@ -609,20 +655,22 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("using null/undefined for methodName or method no-ops", async () => {
await VerifyLogger.run(async (logger) => {
const warnings: string[] = [];
const logger = {
const wrappingLogger = {
log(logLevel: LogLevel, message: string) {
if (logLevel === LogLevel.Warning) {
warnings.push(message);
}
logger.log(logLevel, message);
},
} as ILogger;
const connection = new TestConnection();
const hubConnection = createHubConnection(connection, logger);
const hubConnection = createHubConnection(connection, wrappingLogger);
try {
connection.receiveHandshakeResponse();
@ -655,12 +703,14 @@ describe("HubConnection", () => {
}
});
});
});
describe("stream", () => {
it("sends an invocation", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.stream("testStream", "arg", 42);
@ -682,10 +732,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("completes with an error when an error is yielded", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -700,10 +752,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("completes the observer when a completion is received", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -718,11 +772,13 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("completes pending streams when stopped", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
const observer = new TestObserver();
hubConnection.stream<any>("testMethod")
@ -734,11 +790,13 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("completes pending streams when connection is lost", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
const observer = new TestObserver();
hubConnection.stream<any>("testMethod")
@ -752,10 +810,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("yields items as they arrive", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -778,11 +838,13 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("does not require error function registered", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
@ -793,11 +855,13 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("does not require complete function registered", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
@ -808,10 +872,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("can be canceled", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
connection.receiveHandshakeResponse();
@ -839,11 +905,13 @@ describe("HubConnection", () => {
}
});
});
});
describe("onClose", () => {
it("can have multiple callbacks", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let invocations = 0;
hubConnection.onclose((e) => invocations++);
@ -855,10 +923,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("callbacks receive error", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let error: Error | undefined;
hubConnection.onclose((e) => error = e);
@ -870,10 +940,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("ignores null callbacks", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.onclose(null!);
hubConnection.onclose(undefined!);
@ -884,10 +956,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("state disconnected", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
let state: HubConnectionState | undefined;
hubConnection.onclose((e) => state = hubConnection.state);
@ -900,13 +974,15 @@ describe("HubConnection", () => {
}
});
});
});
describe("keepAlive", () => {
it("can receive ping messages", async () => {
await VerifyLogger.run(async (logger) => {
// Receive the ping mid-invocation so we can see that the rest of the flow works fine
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
const invokePromise = hubConnection.invoke("testMethod", "arg", 42);
@ -918,10 +994,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("does not terminate if messages are received", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.serverTimeoutInMilliseconds = 200;
@ -943,10 +1021,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("does not timeout if message was received before HubConnection.start", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.serverTimeoutInMilliseconds = 200;
@ -972,10 +1052,12 @@ describe("HubConnection", () => {
await hubConnection.stop();
}
});
});
it("terminates if no messages received within timeout interval", async () => {
await VerifyLogger.run(async (logger) => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
const hubConnection = createHubConnection(connection, logger);
try {
hubConnection.serverTimeoutInMilliseconds = 100;
@ -992,6 +1074,7 @@ describe("HubConnection", () => {
}
});
});
});
});
async function pingAndWait(connection: TestConnection): Promise<void> {

View File

@ -10,6 +10,7 @@ import { ILogger, LogLevel } from "../src/ILogger";
import { HttpTransportType, TransferFormat } from "../src/ITransport";
import { NullLogger } from "../src/Loggers";
import { VerifyLogger } from "./Common";
import { TestHttpClient } from "./TestHttpClient";
import { PromiseSource } from "./Utils";
@ -43,6 +44,7 @@ describe("HubConnectionBuilder", () => {
});
it("builds HubConnection with HttpConnection using provided URL", async () => {
await VerifyLogger.run(async (logger) => {
const pollSent = new PromiseSource<HttpRequest>();
const pollCompleted = new PromiseSource<HttpResponse>();
const testClient = createTestClient(pollSent, pollCompleted.promise)
@ -55,6 +57,7 @@ describe("HubConnectionBuilder", () => {
.withUrl("http://example.com", {
...commonHttpOptions,
httpClient: testClient,
logger,
})
.build();
@ -67,6 +70,7 @@ describe("HubConnectionBuilder", () => {
await closed;
});
});
it("can configure transport type", async () => {
const protocol = new TestProtocol();
@ -78,6 +82,7 @@ describe("HubConnectionBuilder", () => {
});
it("can configure hub protocol", async () => {
await VerifyLogger.run(async (logger) => {
const protocol = new TestProtocol();
const pollSent = new PromiseSource<HttpRequest>();
@ -95,6 +100,7 @@ describe("HubConnectionBuilder", () => {
.withUrl("http://example.com", {
...commonHttpOptions,
httpClient: testClient,
logger,
})
.withHubProtocol(protocol)
.build();
@ -108,6 +114,7 @@ describe("HubConnectionBuilder", () => {
await closed;
});
});
it("allows logger to be replaced", async () => {
let loggedMessages = 0;

View File

@ -3,11 +3,12 @@
import { CompletionMessage, InvocationMessage, MessageType, StreamItemMessage } from "../src/IHubProtocol";
import { JsonHubProtocol } from "../src/JsonHubProtocol";
import { NullLogger } from "../src/Loggers";
import { TextMessageFormat } from "../src/TextMessageFormat";
import { VerifyLogger } from "./Common";
describe("JsonHubProtocol", () => {
it("can write/read non-blocking Invocation message", () => {
it("can write/read non-blocking Invocation message", async () => {
await VerifyLogger.run(async (logger) => {
const invocation = {
arguments: [42, true, "test", ["x1", "y2"], null],
headers: {},
@ -16,11 +17,13 @@ describe("JsonHubProtocol", () => {
} as InvocationMessage;
const protocol = new JsonHubProtocol();
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
expect(parsedMessages).toEqual([invocation]);
});
});
it("can read Invocation message with Date argument", () => {
it("can read Invocation message with Date argument", async () => {
await VerifyLogger.run(async (logger) => {
const invocation = {
arguments: [Date.UTC(2018, 1, 1, 12, 34, 56)],
headers: {},
@ -29,11 +32,13 @@ describe("JsonHubProtocol", () => {
} as InvocationMessage;
const protocol = new JsonHubProtocol();
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
expect(parsedMessages).toEqual([invocation]);
});
});
it("can write/read Invocation message with headers", () => {
it("can write/read Invocation message with headers", async () => {
await VerifyLogger.run(async (logger) => {
const invocation = {
arguments: [42, true, "test", ["x1", "y2"], null],
headers: {
@ -44,11 +49,13 @@ describe("JsonHubProtocol", () => {
} as InvocationMessage;
const protocol = new JsonHubProtocol();
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
expect(parsedMessages).toEqual([invocation]);
});
});
it("can write/read Invocation message", () => {
it("can write/read Invocation message", async () => {
await VerifyLogger.run(async (logger) => {
const invocation = {
arguments: [42, true, "test", ["x1", "y2"], null],
headers: {},
@ -58,9 +65,10 @@ describe("JsonHubProtocol", () => {
} as InvocationMessage;
const protocol = new JsonHubProtocol();
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
expect(parsedMessages).toEqual([invocation]);
});
});
([
[`{"type":3, "invocationId": "abc", "error": "Err", "result": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
@ -101,9 +109,11 @@ describe("JsonHubProtocol", () => {
type: MessageType.Completion,
} as CompletionMessage],
] as Array<[string, CompletionMessage]>).forEach(([payload, expectedMessage]) =>
it("can read Completion message", () => {
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
it("can read Completion message", async () => {
await VerifyLogger.run(async (logger) => {
const messages = new JsonHubProtocol().parseMessages(payload, logger);
expect(messages).toEqual([expectedMessage]);
});
}));
([
@ -122,9 +132,11 @@ describe("JsonHubProtocol", () => {
type: MessageType.StreamItem,
} as StreamItemMessage],
] as Array<[string, StreamItemMessage]>).forEach(([payload, expectedMessage]) =>
it("can read StreamItem message", () => {
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
it("can read StreamItem message", async () => {
await VerifyLogger.run(async (logger) => {
const messages = new JsonHubProtocol().parseMessages(payload, logger);
expect(messages).toEqual([expectedMessage]);
});
}));
([
@ -138,9 +150,11 @@ describe("JsonHubProtocol", () => {
type: MessageType.StreamItem,
} as StreamItemMessage],
] as Array<[string, StreamItemMessage]>).forEach(([payload, expectedMessage]) =>
it("can read message with headers", () => {
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
it("can read message with headers", async () => {
await VerifyLogger.run(async (logger) => {
const messages = new JsonHubProtocol().parseMessages(payload, logger);
expect(messages).toEqual([expectedMessage]);
});
}));
([
@ -155,14 +169,17 @@ describe("JsonHubProtocol", () => {
["Completion message with result and error", `{"type":3,"invocationId":"1","result":2,"error":"error"}${TextMessageFormat.RecordSeparator}`, "Invalid payload for Completion message."],
["Completion message with non-string error", `{"type":3,"invocationId":"1","error":21}${TextMessageFormat.RecordSeparator}`, "Invalid payload for Completion message."],
] as Array<[string, string, string]>).forEach(([name, payload, expectedError]) =>
it("throws for " + name, () => {
expect(() => new JsonHubProtocol().parseMessages(payload, NullLogger.instance))
it("throws for " + name, async () => {
await VerifyLogger.run(async (logger) => {
expect(() => new JsonHubProtocol().parseMessages(payload, logger))
.toThrow(expectedError);
});
}));
it("can read multiple messages", () => {
it("can read multiple messages", async () => {
await VerifyLogger.run(async (logger) => {
const payload = `{"type":2, "invocationId": "abc", "headers": {}, "item": 8}${TextMessageFormat.RecordSeparator}{"type":3, "invocationId": "abc", "headers": {}, "result": "OK"}${TextMessageFormat.RecordSeparator}`;
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
const messages = new JsonHubProtocol().parseMessages(payload, logger);
expect(messages).toEqual([
{
headers: {},
@ -178,14 +195,17 @@ describe("JsonHubProtocol", () => {
} as CompletionMessage,
]);
});
});
it("can read ping message", () => {
it("can read ping message", async () => {
await VerifyLogger.run(async (logger) => {
const payload = `{"type":6}${TextMessageFormat.RecordSeparator}`;
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
const messages = new JsonHubProtocol().parseMessages(payload, logger);
expect(messages).toEqual([
{
type: MessageType.Ping,
},
]);
});
});
});

View File

@ -3,14 +3,15 @@
import { HttpResponse } from "../src/HttpClient";
import { TransferFormat } from "../src/ITransport";
import { NullLogger } from "../src/Loggers";
import { LongPollingTransport } from "../src/LongPollingTransport";
import { VerifyLogger } from "./Common";
import { TestHttpClient } from "./TestHttpClient";
import { PromiseSource, SyncPoint } from "./Utils";
describe("LongPollingTransport", () => {
it("shuts down polling by aborting in-progress request", async () => {
await VerifyLogger.run(async (logger) => {
let firstPoll = true;
const pollCompleted = new PromiseSource();
const client = new TestHttpClient()
@ -20,7 +21,13 @@ describe("LongPollingTransport", () => {
return new HttpResponse(200);
} else {
// Turn 'onabort' into a promise.
const abort = new Promise((resolve, reject) => r.abortSignal!.onabort = resolve);
const abort = new Promise((resolve, reject) => {
if (r.abortSignal!.aborted) {
resolve();
} else {
r.abortSignal!.onabort = resolve;
}
});
await abort;
// Signal that the poll has completed.
@ -30,7 +37,7 @@ describe("LongPollingTransport", () => {
}
})
.on("DELETE", () => new HttpResponse(202));
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
const transport = new LongPollingTransport(client, undefined, logger, false);
await transport.connect("http://example.com", TransferFormat.Text);
const stopPromise = transport.stop();
@ -39,8 +46,10 @@ describe("LongPollingTransport", () => {
await stopPromise;
});
});
it("204 server response stops polling and raises onClose", async () => {
await VerifyLogger.run(async (logger) => {
let firstPoll = true;
const client = new TestHttpClient()
.on("GET", async () => {
@ -52,7 +61,7 @@ describe("LongPollingTransport", () => {
return new HttpResponse(204);
}
});
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
const transport = new LongPollingTransport(client, undefined, logger, false);
const stopPromise = makeClosedPromise(transport);
@ -61,8 +70,10 @@ describe("LongPollingTransport", () => {
// Close will be called on transport because of 204 result from polling
await stopPromise;
});
});
it("sends DELETE on stop after polling has finished", async () => {
await VerifyLogger.run(async (logger) => {
let firstPoll = true;
let deleteSent = false;
const pollingPromiseSource = new PromiseSource();
@ -83,7 +94,7 @@ describe("LongPollingTransport", () => {
return new HttpResponse(202);
});
const transport = new LongPollingTransport(httpClient, undefined, NullLogger.instance, false);
const transport = new LongPollingTransport(httpClient, undefined, logger, false);
await transport.connect("http://tempuri.org", TransferFormat.Text);
@ -106,6 +117,7 @@ describe("LongPollingTransport", () => {
// Wait for stop to complete
await stopPromise;
});
});
});
function makeClosedPromise(transport: LongPollingTransport): Promise<void> {