Files
cosmos-explorer/src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts
Laurent Nguyen 90c1439d34 Update prettier to latest. Remove tslint (#1641)
* Rev up prettier

* Reformat

* Remove deprecated tslint

* Remove call to tslint and update package-lock.json
2023-10-03 17:13:24 +02:00

398 lines
14 KiB
TypeScript

// TODO Should be able to remove this in next version of nodejs
// https://github.com/nodejs/node/commit/932be0164fb3ed869ae3ddfff391721d2fd8e1af
// (<any>global).TextEncoder = require('util').TextEncoder
// (<any>global).TextDecoder = require('util').TextDecoder
import * as sinon from "sinon";
import {
GremlinSimpleClient,
GremlinSimpleClientParameters,
Result,
GremlinRequestMessage,
GremlinResponseMessage,
} from "./GremlinSimpleClient";
describe("Gremlin Simple Client", () => {
let sandbox: sinon.SinonSandbox;
let fakeSocket: any;
const createParams = (): GremlinSimpleClientParameters => {
return {
endpoint: "endpoint",
user: "user",
password: "password",
successCallback: (result: Result) => {},
progressCallback: (result: Result) => {},
failureCallback: (result: Result, error: string) => {},
infoCallback: (msg: string) => {},
};
};
const fakeStatus = (
code: number,
requestCharge: number,
): {
attributes: {
"x-ms-request-charge": number;
"x-ms-total-request-charge": number;
};
code: number;
message: string;
} => ({
attributes: {
"x-ms-request-charge": requestCharge,
"x-ms-total-request-charge": -123,
},
code: code,
message: null,
});
beforeEach(() => {
sandbox = sinon.sandbox.create();
fakeSocket = {
fakeResponse: null,
onopen: null,
onerror: null,
onmessage: null,
onclose: null,
send: (msg: any) => {
if (fakeSocket.onmessage) {
fakeSocket.onmessage(fakeSocket.fakeResponse);
}
},
close: () => {},
};
sandbox.stub(GremlinSimpleClient, "createWebSocket").returns(fakeSocket);
});
afterEach(() => {
sandbox.restore();
});
it("should encode utf-8 strings when building Gremlin message", () => {
const msg = "é";
const expected = [16, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 106, 115, 111, 110, 34, 195, 169, 34];
const actual = GremlinSimpleClient.buildGremlinMessage(msg);
// TODO Array.from(actual) isn't supported in phantomJS. Must iterate through for now
const actualArray = [];
for (let i = 0; i < actual.length; i++) {
actualArray.push(actual[i]);
}
expect(actualArray).toEqual(expected);
});
it("should decode response from Gremlin server", () => {
const expectedDecodedData = {
requestId: "d772f897-0d4d-4cd1-b360-ddf6c86b93a3",
status: {
code: 200,
attributes: { graphExecutionStatus: 200, StorageRU: 2.29, ComputeRU: 1.07, PerPartitionComputeCharges: {} },
message: "",
},
result: { data: ["é"], meta: {} },
};
const expectedDecodedUint8ArrayValues = [
123, 34, 114, 101, 113, 117, 101, 115, 116, 73, 100, 34, 58, 34, 100, 55, 55, 50, 102, 56, 57, 55, 45, 48, 100,
52, 100, 45, 52, 99, 100, 49, 45, 98, 51, 54, 48, 45, 100, 100, 102, 54, 99, 56, 54, 98, 57, 51, 97, 51, 34, 44,
34, 115, 116, 97, 116, 117, 115, 34, 58, 123, 34, 99, 111, 100, 101, 34, 58, 50, 48, 48, 44, 34, 97, 116, 116,
114, 105, 98, 117, 116, 101, 115, 34, 58, 123, 34, 103, 114, 97, 112, 104, 69, 120, 101, 99, 117, 116, 105, 111,
110, 83, 116, 97, 116, 117, 115, 34, 58, 50, 48, 48, 44, 34, 83, 116, 111, 114, 97, 103, 101, 82, 85, 34, 58, 50,
46, 50, 57, 44, 34, 67, 111, 109, 112, 117, 116, 101, 82, 85, 34, 58, 49, 46, 48, 55, 44, 34, 80, 101, 114, 80,
97, 114, 116, 105, 116, 105, 111, 110, 67, 111, 109, 112, 117, 116, 101, 67, 104, 97, 114, 103, 101, 115, 34, 58,
123, 125, 125, 44, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 34, 34, 125, 44, 34, 114, 101, 115, 117, 108,
116, 34, 58, 123, 34, 100, 97, 116, 97, 34, 58, 91, 34, 195, 169, 34, 93, 44, 34, 109, 101, 116, 97, 34, 58, 123,
125, 125, 125,
];
// We do our best here to emulate what the server should return
const gremlinResponseData = new Uint8Array(<any>expectedDecodedUint8ArrayValues).buffer;
const client = new GremlinSimpleClient(createParams());
const actualDecoded = client.decodeMessage(<any>{ data: gremlinResponseData });
expect(actualDecoded).toEqual(expectedDecodedData);
});
it("should connect before sending", () => {
const params = createParams();
params.infoCallback = sandbox.spy();
const client = new GremlinSimpleClient(params);
client.executeGremlinQuery("test");
try {
fakeSocket.onopen();
} catch (e) {
// Eat expected json parse exception and not pollute test output
}
expect((<sinon.SinonSpy>params.infoCallback).calledOnce).toBe(true);
});
it("should handle incoming message", () => {
fakeSocket.fakeResponse = "test";
const params = createParams();
const client = new GremlinSimpleClient(params);
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(200, null),
requestId: "id",
result: { data: "mydata" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
const onMessageSpy = sandbox.spy(client, "onMessage");
client.executeGremlinQuery("test");
fakeSocket.onopen();
expect(onMessageSpy.calledOnce).toBe(true);
});
it("should call fail callback on incoming invalid message", () => {
const params = createParams();
const client = new GremlinSimpleClient(params);
const onFailureSpy = sandbox.spy(client.params, "failureCallback");
fakeSocket.fakeResponse = {
status: fakeStatus(200, null),
requestId: Object.keys(client.pendingRequests)[0],
data: new Uint8Array([1, 1, 1, 1]).buffer,
};
client.executeGremlinQuery("test");
fakeSocket.onopen();
expect(onFailureSpy.calledOnce).toBe(true);
});
it("should handle 200 status code", () => {
fakeSocket.send = (msg: any) => {};
const params = createParams();
const client = new GremlinSimpleClient(params);
const onSuccessSpy = sandbox.spy(client.params, "successCallback");
client.executeGremlinQuery("test");
fakeSocket.onopen();
const RU = 99;
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(200, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: "mydata" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
expect(
onSuccessSpy.calledWith({
requestId: fakeResponse.requestId,
data: fakeResponse.result.data,
requestCharge: RU,
}),
).toBe(true);
});
it("should handle 204 status code (no content)", () => {
fakeSocket.send = (msg: any) => {};
const params = createParams();
const client = new GremlinSimpleClient(params);
const onSuccessSpy = sandbox.spy(client.params, "successCallback");
client.executeGremlinQuery("test");
fakeSocket.onopen();
const RU = 99;
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(204, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: "THIS SHOULD BE IGNORED" },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
expect(
onSuccessSpy.calledWith({
requestId: fakeResponse.requestId,
data: null,
requestCharge: RU,
}),
).toBe(true);
});
it("should handle 206 status code (partial)", () => {
fakeSocket.send = (msg: any) => {};
const params = createParams();
const client = new GremlinSimpleClient(params);
const onSuccessSpy = sandbox.spy(client.params, "successCallback");
const onProgressSpy = sandbox.spy(client.params, "progressCallback");
client.executeGremlinQuery("test");
fakeSocket.onopen();
const RU = 99;
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(206, RU),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: [1, 2, 3] },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
expect(
onProgressSpy.calledWith({
requestId: fakeResponse.requestId,
data: fakeResponse.result.data,
requestCharge: RU,
}),
).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 407 status code (auth challenge)", () => {
const socketSendStub = sandbox.stub(fakeSocket, "send").callsFake(() => {});
const params = createParams();
const client = new GremlinSimpleClient(params);
const onSuccessSpy = sandbox.spy(client.params, "successCallback");
const buildChallengeSpy = sandbox.spy(client, "buildChallengeResponse");
client.executeGremlinQuery("test");
fakeSocket.onopen();
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(407, null),
requestId: Object.keys(client.pendingRequests)[0],
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(new MessageEvent("test2"));
expect(onSuccessSpy.notCalled).toBe(true);
expect(buildChallengeSpy.calledOnce).toBe(true);
expect(socketSendStub.calledTwice).toBe(true); // Once to send the query, once to send auth response
});
describe("error status codes", () => {
let params: GremlinSimpleClientParameters;
let client: GremlinSimpleClient;
let onSuccessSpy: sinon.SinonSpy;
let onFailureSpy: sinon.SinonSpy;
beforeEach(() => {
fakeSocket.send = (msg: any) => {};
params = createParams();
client = new GremlinSimpleClient(params);
onSuccessSpy = sandbox.spy(client.params, "successCallback");
onFailureSpy = sandbox.spy(client.params, "failureCallback");
client.executeGremlinQuery("test");
fakeSocket.onopen();
});
it("should handle 401 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(401, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 401 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(401, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 498 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(498, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 500 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(500, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 597 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(597, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 598 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(598, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle 599 status code (error)", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(599, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
it("should handle unknown status code", () => {
const fakeResponse: GremlinResponseMessage = {
status: fakeStatus(123123123, null),
requestId: "id",
result: { data: <any>null },
};
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
client.onMessage(null);
expect(onFailureSpy.calledOnce).toBe(true);
expect(onSuccessSpy.notCalled).toBe(true);
});
});
it("should build auth message", () => {
const params = createParams();
params.user = "éà";
params.password = "=";
const client = new GremlinSimpleClient(params);
const expectedSASLResult = "AMOpw6AAPQ==";
const request: GremlinRequestMessage = {
requestId: "id",
op: "eval",
processor: "processor",
args: {
gremlin: "gremlin",
bindings: {},
language: "language",
},
};
const expectedResult: GremlinRequestMessage = {
requestId: request.requestId,
processor: request.processor,
op: "authentication",
args: {
SASL: expectedSASLResult,
},
};
const actual = client.buildChallengeResponse(request);
expect(actual).toEqual(expectedResult);
});
it("should convert utf8 to b64", () => {
expect(GremlinSimpleClient.utf8ToB64("小飼弾")).toEqual("5bCP6aO85by+");
expect(GremlinSimpleClient.utf8ToB64("à mon hôpital préféré")).toEqual("w6AgbW9uIGjDtHBpdGFsIHByw6lmw6lyw6k=");
});
});