940 lines
33 KiB
TypeScript
940 lines
33 KiB
TypeScript
/*
|
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0.
|
|
*/
|
|
|
|
import * as eventstream from './eventstream';
|
|
import * as cancel from '../common/cancel';
|
|
import {once} from "events";
|
|
import crt_native from "./binding";
|
|
import * as os from "os";
|
|
|
|
jest.setTimeout(10000);
|
|
|
|
function hasEchoServerEnvironment() : boolean {
|
|
if (process.env.AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST === undefined) {
|
|
return false;
|
|
}
|
|
|
|
if (process.env.AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT === undefined) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const conditional_test = (condition : boolean) => condition ? it : it.skip;
|
|
|
|
function closeNativeConnectionInternal(connection: eventstream.ClientConnection) {
|
|
|
|
// invoke an internal close that bypasses the binding. The result is an invocation that simulates a network
|
|
// disruption from the binding's perspective
|
|
crt_native.event_stream_client_connection_close_internal(connection.native_handle());
|
|
}
|
|
|
|
function makeGoodConfig() : eventstream.ClientConnectionOptions {
|
|
let config : eventstream.ClientConnectionOptions = {
|
|
hostName: process.env.AWS_TEST_EVENT_STREAM_ECHO_SERVER_HOST ?? "",
|
|
port: parseInt(process.env.AWS_TEST_EVENT_STREAM_ECHO_SERVER_PORT ?? "0"),
|
|
};
|
|
|
|
return config;
|
|
}
|
|
|
|
/*
|
|
* successful connection setup/teardown tests include some short waits to try and shake out any native race conditions
|
|
* that might occur due to JS object finalization after close. For the same reason, we scope the connection object
|
|
* to a helper function, making finalization on the extern more likely.
|
|
*/
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream transport connection success echo server - await connect, close, and forget', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
let cancelController : cancel.CancelController = new cancel.CancelController();
|
|
|
|
await connection.connect({
|
|
cancelController : cancelController
|
|
});
|
|
|
|
// @ts-ignore
|
|
expect(cancelController.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream transport connection success echo server - await connect, simulate remote close', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let disconnected = once(connection, eventstream.ClientConnection.DISCONNECTION);
|
|
|
|
await connection.connect({});
|
|
|
|
// simulate a socket closed by the remote endpoint scenario
|
|
closeNativeConnectionInternal(connection);
|
|
|
|
await disconnected;
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream transport connection success echo server - start connect, close, and forget', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
// intentionally do not await to try and beat the native connection setup with a close call
|
|
connection.connect({});
|
|
|
|
connection.close();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
});
|
|
|
|
async function doConnectionFailureTest(config : eventstream.ClientConnectionOptions) {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(config);
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
await expect(connection.connect({
|
|
cancelController : controller
|
|
})).rejects.toBeDefined();
|
|
|
|
// @ts-ignore
|
|
expect(controller.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
connection.close();
|
|
}
|
|
|
|
test('Eventstream transport connection failure echo server - bad host', async () => {
|
|
let badConfig : eventstream.ClientConnectionOptions = makeGoodConfig();
|
|
badConfig.hostName = "derp.notarealdomainseriously.org";
|
|
|
|
await doConnectionFailureTest(badConfig);
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream transport connection failure echo server - bad port', async () => {
|
|
let badConfig : eventstream.ClientConnectionOptions = makeGoodConfig();
|
|
badConfig.port = 33333;
|
|
|
|
await doConnectionFailureTest(badConfig);
|
|
});
|
|
|
|
async function doProtocolConnectionSuccessTest1() {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
const connectResponse = once(connection, eventstream.ClientConnection.PROTOCOL_MESSAGE);
|
|
|
|
let connectMessage: eventstream.Message = {
|
|
type: eventstream.MessageType.Connect,
|
|
headers: [
|
|
eventstream.Header.newString(':version', '0.1.0'),
|
|
eventstream.Header.newString('client-name', 'accepted.testy_mc_testerson')
|
|
]
|
|
};
|
|
|
|
await connection.sendProtocolMessage({
|
|
message: connectMessage
|
|
});
|
|
|
|
let response : eventstream.MessageEvent = (await connectResponse)[0];
|
|
let message : eventstream.Message = response.message;
|
|
|
|
expect(message.type).toEqual(eventstream.MessageType.ConnectAck);
|
|
expect(message.flags).toBeDefined();
|
|
expect((message.flags ?? 0) & eventstream.MessageFlags.ConnectionAccepted).toEqual(eventstream.MessageFlags.ConnectionAccepted);
|
|
|
|
connection.close();
|
|
}
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream protocol connection success Echo Server - happy path', async () => {
|
|
await doProtocolConnectionSuccessTest1();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
});
|
|
|
|
async function doProtocolConnectionSuccessTest2() {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
let connectMessage: eventstream.Message = {
|
|
type: eventstream.MessageType.Connect,
|
|
headers: [
|
|
eventstream.Header.newString(':version', '0.1.0'),
|
|
eventstream.Header.newString('client-name', 'accepted.testy_mc_testerson')
|
|
]
|
|
};
|
|
|
|
connection.sendProtocolMessage({
|
|
message: connectMessage
|
|
});
|
|
|
|
connection.close();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
}
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream protocol connection success Echo Server - close while connecting', async () => {
|
|
await doProtocolConnectionSuccessTest2();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
});
|
|
|
|
async function makeGoodConnection() : Promise<eventstream.ClientConnection> {
|
|
return new Promise<eventstream.ClientConnection>(async (resolve, reject) => {
|
|
try {
|
|
let connection: eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
const connectResponse = once(connection, eventstream.ClientConnection.PROTOCOL_MESSAGE);
|
|
|
|
let connectMessage: eventstream.Message = {
|
|
type: eventstream.MessageType.Connect,
|
|
headers: [
|
|
eventstream.Header.newString(':version', '0.1.0'),
|
|
eventstream.Header.newString('client-name', 'accepted.testy_mc_testerson')
|
|
]
|
|
};
|
|
|
|
await connection.sendProtocolMessage({
|
|
message: connectMessage
|
|
});
|
|
|
|
let response: eventstream.MessageEvent = (await connectResponse)[0];
|
|
let message: eventstream.Message = response.message;
|
|
if (((message.flags ?? 0) & eventstream.MessageFlags.ConnectionAccepted) == 0) {
|
|
reject();
|
|
}
|
|
|
|
resolve(connection);
|
|
} catch (e) {
|
|
reject();
|
|
}
|
|
});
|
|
}
|
|
|
|
function buildAllTypeHeaderSet() : Array<eventstream.Header> {
|
|
var encoder = new TextEncoder();
|
|
let buffer: ArrayBuffer = encoder.encode("Some test");
|
|
let uuid: ArrayBuffer = encoder.encode("0123456789ABCDEF");
|
|
|
|
let headers: Array<eventstream.Header> = [
|
|
eventstream.Header.newBoolean('boolTrue', true),
|
|
eventstream.Header.newBoolean('boolFalse', false),
|
|
eventstream.Header.newByte('byte', 8),
|
|
eventstream.Header.newInt16('int16', 32767),
|
|
eventstream.Header.newInt32('int32', -65537),
|
|
eventstream.Header.newInt64FromBigint('int64Bigint', BigInt(65536) * BigInt(65536) * BigInt(2)),
|
|
eventstream.Header.newInt64FromNumber('int64Number', 65536 * 65536 * 2),
|
|
eventstream.Header.newInt64FromNumber('int64NumberNegative1', -1),
|
|
eventstream.Header.newInt64FromNumber('int64NumberNegativeLarge', -123456789),
|
|
eventstream.Header.newString('string', 'Hello'),
|
|
eventstream.Header.newByteBuffer('binary', buffer),
|
|
eventstream.Header.newTimeStampFromDate('date', new Date()),
|
|
eventstream.Header.newTimeStampFromSecondsSinceEpoch('epochSeconds', Date.now()),
|
|
eventstream.Header.newUUID('uuid', uuid)
|
|
];
|
|
|
|
return headers;
|
|
}
|
|
|
|
function verifyEchoedHeaders(expectedHeaders : Array<eventstream.Header>, actualHeaders : Array<eventstream.Header>) {
|
|
expectedHeaders.forEach((header: eventstream.Header) => {
|
|
let actualHeader = actualHeaders.find((value: eventstream.Header) => { return value.name === header.name; });
|
|
expect(actualHeader).toBeDefined();
|
|
|
|
// @ts-ignore
|
|
expect(header.type).toEqual(actualHeader.type);
|
|
|
|
switch(header.type) {
|
|
case eventstream.HeaderType.BooleanFalse:
|
|
case eventstream.HeaderType.BooleanTrue:
|
|
break;
|
|
|
|
case eventstream.HeaderType.ByteBuffer:
|
|
case eventstream.HeaderType.UUID:
|
|
case eventstream.HeaderType.Int64:
|
|
// @ts-ignore
|
|
expect(Buffer.from(header.value as ArrayBuffer)).toEqual(Buffer.from(actualHeader.value as ArrayBuffer));
|
|
break;
|
|
|
|
default:
|
|
// @ts-ignore
|
|
expect(header.value).toEqual(actualHeader.value);
|
|
break;
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
async function verifyPingRoundTrip(connection : eventstream.ClientConnection) : Promise<void> {
|
|
return new Promise<void>(async (resolve, reject) => {
|
|
try {
|
|
const pingResponse = once(connection, eventstream.ClientConnection.PROTOCOL_MESSAGE);
|
|
|
|
var encoder = new TextEncoder();
|
|
let payload: ArrayBuffer = encoder.encode("A payload");
|
|
|
|
let headers: Array<eventstream.Header> = buildAllTypeHeaderSet();
|
|
|
|
let pingMessage: eventstream.Message = {
|
|
type: eventstream.MessageType.Ping,
|
|
headers: headers,
|
|
payload: payload
|
|
};
|
|
|
|
await connection.sendProtocolMessage({
|
|
message: pingMessage
|
|
});
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await pingResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.PingResponse);
|
|
expect(response.headers).toBeDefined();
|
|
|
|
verifyEchoedHeaders(headers, response.headers ?? []);
|
|
|
|
expect(Buffer.from(payload)).toEqual(Buffer.from(response.payload as ArrayBuffer));
|
|
|
|
resolve();
|
|
} catch (e) {
|
|
reject();
|
|
}
|
|
});
|
|
}
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection success - send and receive all-header-types ping', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
|
|
await verifyPingRoundTrip(connection);
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream protocol connection failure Echo Server - bad version', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
await connection.connect({});
|
|
|
|
const connectResponse = once(connection, eventstream.ClientConnection.PROTOCOL_MESSAGE);
|
|
const disconnected = once(connection, eventstream.ClientConnection.DISCONNECTION);
|
|
|
|
let connectMessage: eventstream.Message = {
|
|
type: eventstream.MessageType.Connect,
|
|
headers: [
|
|
eventstream.Header.newString(':version', '0.0.1'),
|
|
eventstream.Header.newString('client-name', 'accepted.testy_mc_testerson')
|
|
]
|
|
};
|
|
|
|
await connection.sendProtocolMessage({
|
|
message: connectMessage
|
|
});
|
|
|
|
/*
|
|
* Sigh.
|
|
* On Windows, our EchoTest server closes the connection in this case with an RST rather than a FIN. Searching
|
|
* the web hints at a possible timing issue (that affects Windows far more than other platforms) when closing
|
|
* server-side (listener-spawned) sockets that leads to an RST over a FIN:
|
|
*
|
|
* https://github.com/libuv/libuv/issues/3034 shows a similar problem, for example.
|
|
*
|
|
* A socket that is closed with an RST is not readable, despite the fact that there was previously received data.
|
|
* So we'll never be able to get the failed ConnAck because the attempt to read from the socket fails immediately.
|
|
*
|
|
* Alternatively, we could restrict these tests to domain/local sockets but that creates its own set of problems,
|
|
* requiring platform-specific permissions tweaks to allow communication between multiple processes.
|
|
*
|
|
* So in the interest of avoiding rabbit holes, we only verify the failed connack on non-Windows platforms.
|
|
*/
|
|
if (os.platform() !== 'win32') {
|
|
let response: eventstream.MessageEvent = (await connectResponse)[0];
|
|
let message: eventstream.Message = response.message;
|
|
|
|
expect(message.type).toEqual(eventstream.MessageType.ConnectAck);
|
|
expect(message.flags).toBeDefined();
|
|
expect((message.flags ?? 0) & eventstream.MessageFlags.ConnectionAccepted).toEqual(0);
|
|
}
|
|
|
|
await disconnected;
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection failure - create with undefined', async () => {
|
|
expect(() => {
|
|
// @ts-ignore
|
|
new eventstream.ClientConnection(undefined)
|
|
}).toThrow();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection failure - create with missing required property', async () => {
|
|
let config : eventstream.ClientConnectionOptions = makeGoodConfig();
|
|
|
|
// @ts-ignore
|
|
config.hostName = undefined;
|
|
|
|
expect(() => {
|
|
new eventstream.ClientConnection(config)
|
|
}).toThrow();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection failure - sendProtocolMessage with undefined', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
// @ts-ignore
|
|
await expect(connection.sendProtocolMessage(undefined )).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection failure - sendProtocolMessage with missing required property', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
// @ts-ignore
|
|
await expect(connection.sendProtocolMessage({
|
|
cancelController: controller
|
|
} )).rejects.toThrow();
|
|
|
|
// @ts-ignore
|
|
expect(controller.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - newStream while not connected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
expect(() => {connection.newStream();}).toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - sendProtocolMessage while not connected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.Connect
|
|
};
|
|
|
|
await expect(connection.sendProtocolMessage({message: message} )).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - connect while connecting', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let connected : Promise<void> = connection.connect({});
|
|
|
|
await expect(connection.connect({})).rejects.toThrow();
|
|
|
|
await connected;
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - connect while connected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
await expect(connection.connect({})).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - connect while disconnected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let disconnected = once(connection, eventstream.ClientConnection.DISCONNECTION);
|
|
|
|
await connection.connect({});
|
|
|
|
// simulate a socket closed by the remote endpoint scenario
|
|
closeNativeConnectionInternal(connection);
|
|
|
|
await disconnected;
|
|
|
|
await expect(connection.connect({})).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - newStream while disconnected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let disconnected = once(connection, eventstream.ClientConnection.DISCONNECTION);
|
|
|
|
await connection.connect({});
|
|
|
|
// simulate a socket closed by the remote endpoint scenario
|
|
closeNativeConnectionInternal(connection);
|
|
|
|
await disconnected;
|
|
|
|
expect(() => {connection.newStream();}).toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - sendProtocolMessage while disconnected', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
let disconnected = once(connection, eventstream.ClientConnection.DISCONNECTION);
|
|
|
|
await connection.connect({});
|
|
|
|
// simulate a socket closed by the remote endpoint scenario
|
|
closeNativeConnectionInternal(connection);
|
|
|
|
await disconnected;
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.Connect
|
|
};
|
|
|
|
await expect(connection.sendProtocolMessage({message: message} )).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - connect while closed', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
connection.close();
|
|
|
|
await expect(connection.connect({})).rejects.toThrow();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - newStream while closed', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
connection.close();
|
|
|
|
expect(() => {connection.newStream();}).toThrow();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream connection state failure - sendProtocolMessage while closed', async () => {
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
connection.close();
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.Connect
|
|
};
|
|
|
|
await expect(connection.sendProtocolMessage({message: message} )).rejects.toThrow();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - create and close, no asserts', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = new eventstream.ClientConnection(makeGoodConfig());
|
|
|
|
await connection.connect({});
|
|
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
async function openPersistentEchoStream(connection: eventstream.ClientConnection) : Promise<eventstream.ClientStream> {
|
|
return new Promise<eventstream.ClientStream>(async (resolve, reject) => {
|
|
try {
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
const activateResponse = once(stream, eventstream.ClientStream.MESSAGE);
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
};
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
await stream.activate({
|
|
operation: "awstest#EchoStreamMessages",
|
|
message : message,
|
|
cancelController : controller
|
|
});
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await activateResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.ApplicationMessage);
|
|
|
|
// @ts-ignore
|
|
expect(controller.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
resolve(stream);
|
|
} catch (e) {
|
|
reject();
|
|
}
|
|
});
|
|
}
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - activate persistent echo stream, wait for response, close properly', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - activate persistent echo stream, wait for response, close unexpectedly, verify stream ended event', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
let streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
|
|
crt_native.event_stream_client_connection_close_internal(connection.native_handle());
|
|
|
|
await streamEnded;
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - activate one-time echo stream, verify response, verify stream ended', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
const activateResponse = once(stream, eventstream.ClientStream.MESSAGE);
|
|
const streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
|
|
const payloadAsString = "{}";
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
payload: payloadAsString
|
|
};
|
|
|
|
await stream.activate({
|
|
operation: "awstest#EchoMessage",
|
|
message : message
|
|
});
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await activateResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.ApplicationMessage);
|
|
expect(response.flags).toBeDefined();
|
|
expect((response.flags ?? 0) & eventstream.MessageFlags.TerminateStream).toEqual(eventstream.MessageFlags.TerminateStream);
|
|
|
|
let payload : string = "";
|
|
if (response.payload !== undefined) {
|
|
var decoder = new TextDecoder();
|
|
payload = decoder.decode(Buffer.from(response.payload as ArrayBuffer));
|
|
}
|
|
expect(payload).toEqual(payloadAsString);
|
|
|
|
await streamEnded;
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - activate persistent echo stream, send message, verify echo response', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
const echoResponse = once(stream, eventstream.ClientStream.MESSAGE);
|
|
|
|
const payloadAsString = "{}";
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
payload: payloadAsString
|
|
};
|
|
|
|
await stream.sendMessage({ message : message });
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await echoResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.ApplicationMessage);
|
|
expect((response.flags ?? 0) & eventstream.MessageFlags.TerminateStream).toEqual(0);
|
|
|
|
let payload : string = "";
|
|
if (response.payload !== undefined) {
|
|
var decoder = new TextDecoder();
|
|
payload = decoder.decode(Buffer.from(response.payload as ArrayBuffer));
|
|
}
|
|
expect(payload).toEqual(payloadAsString);
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream success - client-side terminate a persistent echo stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
const streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
flags: eventstream.MessageFlags.TerminateStream
|
|
};
|
|
|
|
await stream.sendMessage({ message : message });
|
|
|
|
await streamEnded;
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - activate invalid operation', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
const streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
const activateResponse = once(stream, eventstream.ClientStream.MESSAGE);
|
|
|
|
const payloadAsString = "{}";
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
payload: payloadAsString
|
|
};
|
|
|
|
await stream.activate({
|
|
operation: "awstest#NotAValidOperation",
|
|
message : message
|
|
});
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await activateResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.ApplicationError);
|
|
|
|
await streamEnded;
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - send invalid payload', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
const streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
const echoResponse = once(stream, eventstream.ClientStream.MESSAGE);
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
payload: "}"
|
|
};
|
|
|
|
await stream.sendMessage({ message : message });
|
|
|
|
let responseEvent: eventstream.MessageEvent = (await echoResponse)[0];
|
|
let response: eventstream.Message = responseEvent.message;
|
|
|
|
expect(response.type).toEqual(eventstream.MessageType.ApplicationError);
|
|
|
|
await streamEnded;
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - send message on unactivated stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
};
|
|
|
|
await expect(stream.sendMessage({message: message} )).rejects.toThrow();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - double activate stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
};
|
|
|
|
await expect(stream.activate({operation: "awstest#EchoStreamMessages", message: message} )).rejects.toThrow();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - send message on ended stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
const streamEnded = once(stream, eventstream.ClientStream.ENDED);
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage,
|
|
flags: eventstream.MessageFlags.TerminateStream
|
|
};
|
|
|
|
await stream.sendMessage({ message : message });
|
|
|
|
await streamEnded;
|
|
|
|
await expect(stream.sendMessage({message: message} )).rejects.toThrow();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - activate a closed stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
stream.close();
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
};
|
|
|
|
await expect(stream.activate({operation: "awstest#EchoStreamMessages", message: message} )).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - send message on a closed stream', async () => {
|
|
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
stream.close();
|
|
|
|
let message : eventstream.Message = {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
};
|
|
|
|
await expect(stream.sendMessage({message: message} )).rejects.toThrow();
|
|
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - activate with undefined', async () => {
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
// @ts-ignore
|
|
await expect(stream.activate(undefined)).rejects.toThrow();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - activate with missing required property', async () => {
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = connection.newStream();
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
// @ts-ignore
|
|
let activateOptions : eventstream.ActivateStreamOptions = {
|
|
message: {
|
|
type: eventstream.MessageType.ApplicationMessage
|
|
},
|
|
cancelController: controller
|
|
}
|
|
await expect(stream.activate(activateOptions)).rejects.toThrow();
|
|
|
|
// @ts-ignore
|
|
expect(controller.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - sendMessage with undefined', async () => {
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
|
|
// @ts-ignore
|
|
await expect(stream.sendMessage(undefined)).rejects.toThrow();
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
conditional_test(hasEchoServerEnvironment())('Eventstream stream failure - sendMessage with missing required property', async () => {
|
|
let connection : eventstream.ClientConnection = await makeGoodConnection();
|
|
let stream : eventstream.ClientStream = await openPersistentEchoStream(connection);
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
// @ts-ignore
|
|
await expect(stream.sendMessage({
|
|
cancelController: controller
|
|
})).rejects.toThrow();
|
|
|
|
// @ts-ignore
|
|
expect(controller.emitter.listenerCount(cancel.EVENT_NAME)).toEqual(0);
|
|
|
|
stream.close();
|
|
connection.close();
|
|
});
|
|
|
|
test('Eventstream connection cancel - example.com, cancel after connect', async () => {
|
|
// hangs atm
|
|
let connection: eventstream.ClientConnection = new eventstream.ClientConnection({
|
|
hostName: "example.com",
|
|
port: 22
|
|
});
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
|
|
setTimeout(() => { controller.cancel(); }, 1000);
|
|
|
|
await expect(connection.connect({
|
|
cancelController : controller
|
|
})).rejects.toThrow("cancelled");
|
|
});
|
|
|
|
test('Eventstream connection cancel - example.com, cancel before connect', async () => {
|
|
// hangs atm
|
|
let connection: eventstream.ClientConnection = new eventstream.ClientConnection({
|
|
hostName: "example.com",
|
|
port: 22
|
|
});
|
|
|
|
let controller : cancel.CancelController = new cancel.CancelController();
|
|
controller.cancel();
|
|
|
|
await expect(connection.connect({
|
|
cancelController : controller
|
|
})).rejects.toThrow("cancelled");
|
|
});
|