Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions docs/ChaosHandlerSamples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Testing Handler

### How to include

> Uses [Custom Middleware Chain](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomMiddlewareChain.md), it's not included in default middleware chain

### Modes in Testing Handler

- Manual mode - Setting the Response code manually. - Global/Client Level - Provide a map declared manually containing response code for the requests. - Request Level - Providing response code per request. This would be overriding the Global level response code (if any).
- Random mode - We get a random Response code from a set of response code defined for each method.

### Samples

```js
require("isomorphic-fetch");
const MicrosoftGraph = require("../../lib/src/index.js");
const secrets = require("./secrets");
const fs = require("fs");
// Initialising the client
const client = MicrosoftGraph.Client.init({
defaultVersion: "v1.0",
debugLogging: true,
authProvider: (done) => {
done(null, secrets.accessToken);
},
});

// Declaring the Map, containing response codes for the urls
const manualMap = new Map([["/me/messages/.*", new Map([["GET", 429], ["PATCH", 429]])], ["/me", new Map([["POST", 502]])]]);

// Declaring the chaosHandler and passing the map (if using map, we have to put default strategy as MANUAL)
const chaosHandler = new MicrosoftGraph.ChaosHandler(new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.MANUAL), manualMap);

// Modifying the default middleware chain to add chaos handler, just before httpMessageHandler, otherwise there can be a problem
let arr = client.getMiddlewareChain();
arr.splice(arr.length - 1, 0, chaosHandler);
client.setMiddlewareChain(arr);

// This request would use the Map (Manual mode)
const mail = {
subject: "Chaos Handler Samples",
toRecipients: [
{
emailAddress: {
address: "admin@M365x003297.OnMicrosoft.com",
},
},
],
body: {
content: "<h1>Testing Handler Samples Sample</h1><br>https://github.com/microsoftgraph/msgraph-sdk-javascript",
contentType: "html",
},
};
client
.api("/users/me/sendMail")
.post({
message: mail,
})
.then((res) => {
console.log(res, "This is for sendMail");
})
.catch((err) => {
console.log(err, "This is for sendMail in error case");
});

// OverRiding to Random mode, providing the chaos percentage as 60(percentage times the error would be generated from handler)
client
.api("/me")
.middlewareOptions([new MicrosoftGraph.ChaosHandlerOptions(MicrosoftGraph.ChaosStrategy.RANDOM, undefined, "I generated the error", 60)])
.get()
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});

// This request is passed to the graph and gets a response from the graph, as no entry for /me GET request in the Map
client
.api("/me")
.get()
.then((res) => {
console.log("Found", res, "users");
})
.catch((err) => {
console.log(err, "!!!!!!!!!");
});

// Using Manual Map with regex matching
client
.api("/me/messages/hjdlfslod-fdssdkjfs-6zdkmghs-sadhsu2")
.header("content-type", "application/json")
.update({
birthday: "1908-12-22T00:00:00Z",
})
.then((res) => {
console.log("This is regex matching... Updated Bday");
})
.catch((err) => {
console.log(err, "matched");
});
```
2 changes: 1 addition & 1 deletion docs/content/Batching.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const serialBatching = async function(elem) {
let batchResponseContent = new MicrosoftGraph.BatchResponseContent(response);

//Getting response by id
console.log(batchResponse.getResponseById(downloadId));
console.log(batchResponseContent.getResponseById(downloadId));

//Getting all the responses
console.log(batchResponseContent.getResponses());
Expand Down
254 changes: 254 additions & 0 deletions spec/middleware/ChaosHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import { assert } from "chai";

import { Context } from "../../src/IContext";
import { ChaosHandler } from "../../src/middleware/ChaosHandler";
import { MiddlewareControl } from "../../src/middleware/MiddlewareControl";
import { ChaosHandlerOptions } from "../../src/middleware/options/ChaosHandlerOptions";
import { ChaosStrategy } from "../../src/middleware/options/ChaosStrategy";
import { RequestMethod } from "../../src/RequestMethod";
import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler";

const chaosHandlerOptions = new ChaosHandlerOptions();
const chaosHandler = new ChaosHandler();

describe("ChaosHandler.ts", () => {
/* tslint:disable: no-string-literal */
describe("constructor", () => {
it("Should create an instance with given options", () => {
const handler = new ChaosHandler(chaosHandlerOptions);
assert.isDefined(handler["options"]);
});

it("Should create an instance with default set of options", () => {
const handler = new ChaosHandler();
assert.isDefined(handler["options"]);
});
});

describe("createResponseHeaders", () => {
it("Should have request-id for every random statusCode", () => {
const responseHeader = chaosHandler["createResponseHeaders"](204, "xxxxxxxxxxxxxxxx", new Date().toString());
assert.isDefined(responseHeader.get("request-id"));
});

it("Should have retry-after for 429 case", () => {
const responseHeader = chaosHandler["createResponseHeaders"](429, "xxxxxxxxxxxxxxxx", new Date().toString());
assert.isDefined(responseHeader.get("retry-after"));
});
});

describe("createResponseBody", () => {
it("Should return error in response body for error scenarios", () => {
const responseBody = chaosHandler["createResponseBody"](404, "Not Found", "xxxxxxxxxxxxxx", new Date().toString());
assert.isDefined(responseBody["error"]);
});

it("Should return empty response body for success scenarios", () => {
const responseBody = chaosHandler["createResponseBody"](200, "Not Found", "xxxxxxxxxxxxxx", new Date().toString());
assert.equal(Object.keys(responseBody).length, 0);
});
});

describe("createResponse", () => {
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
};

it("Should return a valid response object for MANUAL case", () => {
chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404), cxt);
assert.isDefined(cxt.response);
});

it("Should return a valid response object for RANDOM case", () => {
chaosHandler["createResponse"](new ChaosHandlerOptions(ChaosStrategy.RANDOM), cxt);
assert.isDefined(cxt.response);
});
});

describe("sendRequest", async () => {
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
};

const manualMap: Map<string, Map<string, number>> = new Map([["/me", new Map([["GET", 500]])]]);
const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
const tempChaosHandler = new ChaosHandler(tempManualOptions, manualMap);

const dummyHTTPHandler = new DummyHTTPMessageHandler();
const handler = new ChaosHandler();
handler.setNext(dummyHTTPHandler);

it("Should return a response after creating it", async () => {
tempChaosHandler["sendRequest"](tempManualOptions, cxt);
assert.isDefined(cxt.response);
});

it("Should send the request to the graph", async () => {
handler["sendRequest"](new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100), cxt);
assert.isDefined(cxt.response);
});
});

describe("getRandomStatusCode", () => {
it("Should return a status code for GET method", () => {
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.GET));
});

it("Should return a status code for POST method", () => {
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.POST));
});

it("Should return a status code for PUT method", () => {
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PUT));
});

it("Should return a status code for PATCH method", () => {
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.PATCH));
});

it("Should return a status code for DELETE method", () => {
assert.isDefined(chaosHandler["getRandomStatusCode"](RequestMethod.DELETE));
});
});

describe("getRelativeURL", () => {
it("Should return a relative URL for the complete URL", () => {
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me"), "/me");
});

it("Should return a relative URL for the complete URL with filter", () => {
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages?filter=emailAddress eq 'jon@contoso.com'"), "/me/messages");
});

it("Should return a relative URL for the complete URL with ids", () => {
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/v1.0/me/messages/q1abcxx-xxxxxx-xxxxabc"), "/me/messages/q1abcxx-xxxxxx-xxxxabc");
});

it("Should return a relative URL for the complete URL in case of beta", () => {
assert.equal(chaosHandler["getRelativeURL"]("https://graph.microsoft.com/beta/me/messages"), "/me/messages");
});
});

describe("setStatusCode", () => {
const manualMap: Map<string, Map<string, number>> = new Map([["/me/messages/.*", new Map([["GET", 500], ["PATCH", 201]])], ["/me", new Map([["GET", 500], ["PATCH", 201]])]]);
const tempManualOptions: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
const tempManualOptionsRegex: ChaosHandlerOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL);
const tempChaosHandlerManual = new ChaosHandler(tempManualOptions, manualMap);
const tempChaosHandlerManualRegex = new ChaosHandler(tempManualOptionsRegex, manualMap);

it("Should set a statusCode for MANUAL mode", () => {
const tempOptions = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 404);
chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.GET);
assert.isDefined(tempOptions.statusCode);
});

it("Should set a statusCode for RANDOM mode", () => {
const tempOptions = new ChaosHandlerOptions(ChaosStrategy.RANDOM, undefined, "I generated the error", 100);
chaosHandler["setStatusCode"](tempOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.POST);
assert.isDefined(tempOptions.statusCode);
});

it("Should set a statusCode for MANUAL mode with manualMap", () => {
tempChaosHandlerManual["setStatusCode"](tempManualOptions, "https://graph.microsoft.com/v1.0/me", RequestMethod.PATCH);
assert.equal(tempManualOptions.statusCode, 201);
});

it("Should set a statusCode for MANUAL mode with manualMap matching regex", () => {
tempChaosHandlerManualRegex["setStatusCode"](tempManualOptionsRegex, "https://graph.microsoft.com/v1.0/me/messages/abc123-xxxxx-xxxxx", RequestMethod.GET);
assert.equal(tempManualOptionsRegex.statusCode, 500);
});
});

describe("getOptions", () => {
it("Should return the options in the context object", () => {
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 405);
const cxt: Context = {
request: "url",
middlewareControl: new MiddlewareControl([options]),
};
const o = chaosHandler["getOptions"](cxt);
assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL);
assert.equal(o.statusCode, 405);
});

it("Should return the default set of options with RANDOM in the middleware", () => {
const cxt: Context = {
request: "url",
};
const o = chaosHandler["getOptions"](cxt);
assert.equal(o.chaosStrategy, ChaosStrategy.RANDOM);
assert.equal(o.statusCode, undefined);
});

it("Should return the default set of options with DEFAULT in the middleware", () => {
const tempChaosHandler = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL));
const cxt: Context = {
request: "url",
};
const o = tempChaosHandler["getOptions"](cxt);
assert.equal(o.chaosStrategy, ChaosStrategy.MANUAL);
assert.equal(o.statusCode, undefined);
});
});

describe("execute", async () => {
const manualMap: Map<string, Map<string, number>> = new Map([["/me", new Map([["GET", 500], ["PATCH", 201]])]]);
const dummyHTTPHandler = new DummyHTTPMessageHandler();
const tempChaosHandlerDefault = new ChaosHandler(new ChaosHandlerOptions());
const tempChaosHandlerRandom = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.RANDOM));
const tempChaosHandlerManual = new ChaosHandler(new ChaosHandlerOptions(ChaosStrategy.MANUAL), manualMap);
tempChaosHandlerDefault.setNext(dummyHTTPHandler);
tempChaosHandlerRandom.setNext(dummyHTTPHandler);
tempChaosHandlerManual.setNext(dummyHTTPHandler);

it("Should return response for Default Case", async () => {
const options = new ChaosHandlerOptions(ChaosStrategy.RANDOM);
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
middlewareControl: new MiddlewareControl([options]),
};
assert.isDefined(tempChaosHandlerDefault["execute"](cxt));
});

it("Should return response for Random case", async () => {
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
};
assert.isDefined(tempChaosHandlerRandom["execute"](cxt));
});

it("Should return response for Manual Global case", async () => {
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
};
assert.isDefined(tempChaosHandlerManual["execute"](cxt));
});

it("Should return response for Manual Request Level case", async () => {
const options = new ChaosHandlerOptions(ChaosStrategy.MANUAL, 200);
const cxt: Context = {
request: "https://graph.microsoft.com/v1.0/me",
options: {
method: "GET",
},
middlewareControl: new MiddlewareControl([options]),
};
assert.isDefined(tempChaosHandlerManual["execute"](cxt));
});
});
});
Loading