mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 03:41:19 +00:00
Initial Move from Azure DevOps to GitHub
This commit is contained in:
@@ -0,0 +1,617 @@
|
||||
// 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=");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user