Skip to content

Commit dd5b1f6

Browse files
authored
[Identity] Refactoring the HTTP request mocking to be easier to understand in context (Azure#19824)
Updated the test HTTP mocks to use classes instead of closure. Fixed Azure#19420
1 parent 6022417 commit dd5b1f6

File tree

9 files changed

+263
-289
lines changed

9 files changed

+263
-289
lines changed

sdk/identity/identity/test/httpRequests.browser.ts

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@
44
import * as sinon from "sinon";
55
import { setLogLevel, AzureLogger, getLogLevel, AzureLogLevel } from "@azure/logger";
66
import { RestError } from "@azure/core-rest-pipeline";
7-
import { AccessToken } from "@azure/core-auth";
7+
import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-auth";
88
import { getError } from "./authTestUtils";
9-
import {
10-
IdentityTestContext,
11-
SendCredentialRequests,
12-
RawTestResponse,
13-
TestResponse,
14-
} from "./httpRequestsCommon";
9+
import { IdentityTestContextInterface, RawTestResponse, TestResponse } from "./httpRequestsCommon";
1510

1611
/**
1712
* Helps specify a different number of responses for Node and for the browser.
@@ -40,86 +35,112 @@ export function prepareMSALResponses(): RawTestResponse[] {
4035
* that may expect more than one response (or error) from more than one endpoint.
4136
* @internal
4237
*/
43-
export async function prepareIdentityTests({
44-
replaceLogger,
45-
logLevel,
46-
}: {
47-
replaceLogger?: boolean;
48-
logLevel?: AzureLogLevel;
49-
}): Promise<IdentityTestContext> {
50-
const sandbox = sinon.createSandbox();
51-
const clock = sandbox.useFakeTimers();
52-
const oldLogLevel = getLogLevel();
53-
const oldLogger = AzureLogger.log;
54-
const logMessages: string[] = [];
38+
export class IdentityTestContext implements IdentityTestContextInterface {
39+
public sandbox: sinon.SinonSandbox;
40+
public clock: sinon.SinonFakeTimers;
41+
public oldLogLevel: AzureLogLevel | undefined;
42+
public oldLogger: any;
43+
public logMessages: string[];
44+
public server: sinon.SinonFakeServer;
45+
public requests: sinon.SinonFakeXMLHttpRequest[];
46+
public responses: RawTestResponse[];
5547

56-
if (logLevel) {
57-
setLogLevel(logLevel);
58-
}
48+
constructor({ replaceLogger, logLevel }: { replaceLogger?: boolean; logLevel?: AzureLogLevel }) {
49+
this.sandbox = sinon.createSandbox();
50+
this.clock = this.sandbox.useFakeTimers();
51+
this.oldLogLevel = getLogLevel();
52+
this.oldLogger = AzureLogger.log;
53+
this.logMessages = [];
5954

60-
if (replaceLogger) {
61-
AzureLogger.log = (args) => {
62-
logMessages.push(args);
63-
};
55+
/**
56+
* Browser specific code.
57+
* Sets up a fake server that will be used to answer any outgoing request.
58+
*/
59+
this.server = this.sandbox.useFakeServer();
60+
this.requests = [];
61+
this.responses = [];
62+
63+
if (logLevel) {
64+
setLogLevel(logLevel);
65+
}
66+
67+
if (replaceLogger) {
68+
AzureLogger.log = (args) => {
69+
this.logMessages.push(args);
70+
};
71+
}
6472
}
6573

66-
/**
67-
* Browser specific code.
68-
* Sets up a fake server that will be used to answer any outgoing request.
69-
*/
70-
const server = sandbox.useFakeServer();
71-
const requests: sinon.SinonFakeXMLHttpRequest[] = [];
72-
const responses: RawTestResponse[] = [];
74+
async restore(): Promise<void> {
75+
this.sandbox.restore();
76+
AzureLogger.log = this.oldLogger;
77+
setLogLevel(this.oldLogLevel);
78+
}
7379

7480
/**
7581
* Wraps a credential's getToken in a mocked environment, then returns the results from the request,
7682
* including potentially an AccessToken, an error and the list of outgoing requests in a simplified format.
7783
*/
78-
async function sendIndividualRequest<T>(
84+
async sendIndividualRequest<T>(
7985
sendPromise: () => Promise<T | null>,
8086
{ response }: { response: TestResponse }
8187
): Promise<T | null> {
8288
/**
8389
* Both keeps track of the outgoing requests,
8490
* and ensures each request answers with each received response, in order.
8591
*/
86-
server.respondWith((xhr) => {
87-
requests.push(xhr);
92+
this.server.respondWith((xhr) => {
93+
this.requests.push(xhr);
8894
xhr.respond(response.statusCode, response.headers, response.body);
8995
});
9096
const promise = sendPromise();
91-
server.respond();
92-
await clock.runAllAsync();
97+
this.server.respond();
98+
await this.clock.runAllAsync();
9399
return promise;
94100
}
95101

96102
/**
97103
* Wraps the outgoing request in a mocked environment, then returns the error that results from the request.
98104
*/
99-
async function sendIndividualRequestAndGetError<T>(
105+
async sendIndividualRequestAndGetError<T>(
100106
sendPromise: () => Promise<T | null>,
101107
response: { response: TestResponse }
102108
): Promise<Error> {
103-
return getError(sendIndividualRequest(sendPromise, response));
109+
return getError(this.sendIndividualRequest(sendPromise, response));
104110
}
105111

106112
/**
107113
* Wraps a credential's getToken in a mocked environment, then returns the results from the request.
108114
*/
109-
const sendCredentialRequests: SendCredentialRequests = async ({
115+
async sendCredentialRequests({
110116
scopes,
111117
getTokenOptions,
112118
credential,
113119
insecureResponses = [],
114120
secureResponses = [],
115-
}) => {
116-
responses.push(...[...insecureResponses, ...secureResponses]);
117-
server.respondWith((xhr) => {
118-
requests.push(xhr);
119-
if (!responses.length) {
121+
}: {
122+
scopes: string | string[];
123+
getTokenOptions?: GetTokenOptions;
124+
credential: TokenCredential;
125+
insecureResponses?: RawTestResponse[];
126+
secureResponses?: RawTestResponse[];
127+
}): Promise<{
128+
result: AccessToken | null;
129+
error?: RestError;
130+
requests: {
131+
url: string;
132+
body: string;
133+
method: string;
134+
headers: Record<string, string>;
135+
}[];
136+
}> {
137+
this.responses.push(...[...insecureResponses, ...secureResponses]);
138+
this.server.respondWith((xhr) => {
139+
this.requests.push(xhr);
140+
if (!this.responses.length) {
120141
throw new Error("No responses to send");
121142
}
122-
const { response, error } = responses.shift()!;
143+
const { response, error } = this.responses.shift()!;
123144
if (response) {
124145
xhr.respond(response.statusCode, response.headers, response.body);
125146
} else if (error) {
@@ -137,8 +158,8 @@ export async function prepareIdentityTests({
137158
// We need the promises to begin triggering, so the server has something to respond to,
138159
// and only then we can wait for all of the async processes to finish.
139160
const promise = credential.getToken(scopes, getTokenOptions);
140-
server.respond();
141-
await clock.runAllAsync();
161+
this.server.respond();
162+
await this.clock.runAllAsync();
142163
result = await promise;
143164
} catch (e) {
144165
error = e;
@@ -147,7 +168,7 @@ export async function prepareIdentityTests({
147168
return {
148169
result,
149170
error,
150-
requests: requests.map((request) => {
171+
requests: this.requests.map((request) => {
151172
return {
152173
url: request.url,
153174
body: request.requestBody,
@@ -156,21 +177,5 @@ export async function prepareIdentityTests({
156177
};
157178
}),
158179
};
159-
};
160-
161-
return {
162-
clock,
163-
logMessages,
164-
oldLogLevel,
165-
sandbox,
166-
oldLogger,
167-
async restore() {
168-
sandbox.restore();
169-
AzureLogger.log = oldLogger;
170-
setLogLevel(oldLogLevel);
171-
},
172-
sendIndividualRequest,
173-
sendIndividualRequestAndGetError,
174-
sendCredentialRequests,
175-
};
180+
}
176181
}

0 commit comments

Comments
 (0)