Skip to content

Commit 682b5bd

Browse files
committed
feat: oauth unit test cases
1 parent 2736ef7 commit 682b5bd

File tree

2 files changed

+326
-0
lines changed

2 files changed

+326
-0
lines changed

test/unit/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ require('./variantGroup-test')
4444
require('./ungroupedVariants-test')
4545
require('./variantsWithVariantsGroup-test')
4646
require('./variants-entry-test')
47+
require('./oauthHandler-test')

test/unit/oauthHandler-test.js

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
import { expect } from 'chai';
2+
import sinon from 'sinon';
3+
import axios from 'axios';
4+
import OAuthHandler from '../../lib/core/oauthHandler';
5+
6+
describe('OAuthHandler', () => {
7+
let axiosInstance;
8+
let oauthHandler;
9+
let sandbox;
10+
11+
beforeEach(() => {
12+
sandbox = sinon.createSandbox();
13+
axiosInstance = axios.create({
14+
uiBaseUrl: 'https://example.com', // Make sure this is correctly set
15+
developerHubBaseUrl: 'https://developer.example.com',
16+
baseURL: 'https://api.example.com',
17+
});
18+
oauthHandler = new OAuthHandler(axiosInstance, 'appId', 'clientId', 'http://localhost:8184', 'clientSecret');
19+
});
20+
21+
afterEach(() => {
22+
sandbox.restore();
23+
});
24+
25+
it('should initialize OAuthHandler with correct properties', () => {
26+
expect(oauthHandler.appId).to.equal('appId');
27+
expect(oauthHandler.clientId).to.equal('clientId');
28+
expect(oauthHandler.redirectUri).to.equal('http://localhost:8184');
29+
expect(oauthHandler.responseType).to.equal('code');
30+
expect(oauthHandler.scope).to.equal('');
31+
expect(oauthHandler.clientSecret).to.equal('clientSecret');
32+
expect(oauthHandler.OAuthBaseURL).to.equal('https://example.com');
33+
expect(oauthHandler.axiosInstance).to.equal(axiosInstance);
34+
});
35+
36+
it('should generate code verifier', () => {
37+
const codeVerifier = oauthHandler.generateCodeVerifier();
38+
expect(codeVerifier).to.have.lengthOf(128);
39+
});
40+
41+
it('should generate code challenge', async () => {
42+
const codeVerifier = 'testCodeVerifier';
43+
const codeChallenge = await oauthHandler.generateCodeChallenge(codeVerifier);
44+
expect(codeChallenge).to.exist;
45+
});
46+
47+
it('should authorize and return authorization URL', async () => {
48+
const authUrl = await oauthHandler.authorize();
49+
expect(authUrl).to.include('https://example.com/');
50+
expect(authUrl).to.include('response_type=code');
51+
expect(authUrl).to.include('client_id=clientId');
52+
});
53+
54+
it('should exchange code for token', async () => {
55+
const tokenData = { access_token: 'accessToken', refresh_token: 'refreshToken', expires_in: 3600 };
56+
sandbox.stub(axiosInstance, 'post').resolves({ data: tokenData });
57+
58+
const result = await oauthHandler.exchangeCodeForToken('authorization_code');
59+
expect(result).to.deep.equal(tokenData);
60+
expect(result).to.include('data');
61+
});
62+
63+
it('should refresh access token', async () => {
64+
const tokenData = { access_token: 'newAccessToken', refresh_token: 'newRefreshToken', expires_in: 3600 };
65+
sandbox.stub(axiosInstance, 'post').resolves({ data: tokenData });
66+
67+
const result = await oauthHandler.refreshAccessToken('refreshToken');
68+
expect(result).to.deep.equal(tokenData);
69+
});
70+
71+
it('should logout successfully', async () => {
72+
sandbox.stub(oauthHandler, 'getOauthAppAuthorization').resolves('authorizationId');
73+
sandbox.stub(oauthHandler, 'revokeOauthAppAuthorization').resolves({});
74+
75+
const result = await oauthHandler.logout();
76+
expect(result).to.equal('Logged out successfully');
77+
});
78+
79+
it('should handle redirect and exchange code for token', async () => {
80+
const exchangeStub = sandbox.stub(oauthHandler, 'exchangeCodeForToken').resolves({});
81+
82+
await oauthHandler.handleRedirect('http://localhost:8184?code=authorization_code');
83+
expect(exchangeStub.calledWith('authorization_code')).to.be.true;
84+
});
85+
86+
it('should get access token', () => {
87+
oauthHandler.axiosInstance.oauth = { accessToken: 'accessToken' };
88+
const accessToken = oauthHandler.getAccessToken();
89+
expect(accessToken).to.equal('accessToken');
90+
});
91+
92+
it('should get refresh token', () => {
93+
oauthHandler.axiosInstance.oauth = { refreshToken: 'refreshToken' };
94+
const refreshToken = oauthHandler.getRefreshToken();
95+
expect(refreshToken).to.equal('refreshToken');
96+
});
97+
98+
it('should get organization UID', () => {
99+
oauthHandler.axiosInstance.oauth = { organizationUID: 'organizationUID' };
100+
const organizationUID = oauthHandler.getOrganizationUID();
101+
expect(organizationUID).to.equal('organizationUID');
102+
});
103+
104+
it('should get user UID', () => {
105+
oauthHandler.axiosInstance.oauth = { userUID: 'userUID' };
106+
const userUID = oauthHandler.getUserUID();
107+
expect(userUID).to.equal('userUID');
108+
});
109+
110+
it('should get token expiry time', () => {
111+
oauthHandler.axiosInstance.oauth = { tokenExpiryTime: 1234567890 };
112+
const tokenExpiryTime = oauthHandler.getTokenExpiryTime();
113+
expect(tokenExpiryTime).to.equal(1234567890);
114+
});
115+
116+
it('should set access token', () => {
117+
oauthHandler.setAccessToken('newAccessToken');
118+
expect(oauthHandler.axiosInstance.oauth.accessToken).to.equal('newAccessToken');
119+
});
120+
121+
it('should set refresh token', () => {
122+
oauthHandler.setRefreshToken('newRefreshToken');
123+
expect(oauthHandler.axiosInstance.oauth.refreshToken).to.equal('newRefreshToken');
124+
});
125+
126+
it('should set organization UID', () => {
127+
oauthHandler.setOrganizationUID('newOrganizationUID');
128+
expect(oauthHandler.axiosInstance.oauth.organizationUID).to.equal('newOrganizationUID');
129+
});
130+
131+
it('should set user UID', () => {
132+
oauthHandler.setUserUID('newUserUID');
133+
expect(oauthHandler.axiosInstance.oauth.userUID).to.equal('newUserUID');
134+
});
135+
136+
it('should set token expiry time', () => {
137+
oauthHandler.setTokenExpiryTime(1234567890);
138+
expect(oauthHandler.axiosInstance.oauth.tokenExpiryTime).to.equal(1234567890);
139+
});
140+
141+
it('should generate codeVerifier and set codeChallenge to null if clientSecret is not provided', () => {
142+
const oauthHandlerWithoutClientSecret = new OAuthHandler(
143+
axiosInstance,
144+
'appId',
145+
'clientId',
146+
'http://localhost:8184',
147+
null, // No clientSecret
148+
);
149+
150+
// Ensure codeVerifier is generated
151+
expect(oauthHandlerWithoutClientSecret.codeVerifier).to.exist;
152+
153+
// Ensure codeChallenge is null initially
154+
expect(oauthHandlerWithoutClientSecret.codeChallenge).to.equal(null);
155+
});
156+
157+
it('should not generate codeVerifier or codeChallenge if clientSecret is provided', () => {
158+
const oauthHandlerWithClientSecret = new OAuthHandler(
159+
axiosInstance,
160+
'appId',
161+
'clientId',
162+
'http://localhost:8184',
163+
'clientSecret', // clientSecret is provided
164+
);
165+
166+
// codeVerifier and codeChallenge should not be set if clientSecret is provided
167+
expect(oauthHandlerWithClientSecret.codeVerifier).to.equal(undefined);
168+
expect(oauthHandlerWithClientSecret.codeChallenge).to.equal(undefined);
169+
});
170+
171+
it('should generate codeChallenge after calling generateCodeChallenge when clientSecret is not provided', async () => {
172+
const oauthHandlerWithoutClientSecret = new OAuthHandler(
173+
axiosInstance,
174+
'appId',
175+
'clientId',
176+
'http://localhost:8184',
177+
null, // No clientSecret
178+
);
179+
180+
const codeVerifier = oauthHandlerWithoutClientSecret.codeVerifier;
181+
expect(codeVerifier).to.exist; // Ensure codeVerifier is generated
182+
183+
const codeChallenge = await oauthHandlerWithoutClientSecret.generateCodeChallenge(codeVerifier);
184+
185+
// Ensure the codeChallenge is a URL-safe Base64 string
186+
expect(codeChallenge).to.match(/^[A-Za-z0-9-_]+$/); // URL-safe Base64
187+
});
188+
189+
it('should use the Web Crypto API in a browser environment', async () => {
190+
// Mock the browser environment
191+
global.window = {
192+
crypto: {
193+
subtle: {
194+
digest: sinon.stub().resolves(new Uint8Array([1, 2, 3, 4])), // Mock hash result
195+
},
196+
},
197+
};
198+
199+
const oauthHandlerWithoutClientSecret = new OAuthHandler(
200+
axiosInstance,
201+
'appId',
202+
'clientId',
203+
'http://localhost:8184',
204+
null, // No clientSecret
205+
);
206+
207+
const codeVerifier = oauthHandlerWithoutClientSecret.codeVerifier;
208+
expect(codeVerifier).to.exist; // Ensure codeVerifier is generated
209+
210+
const codeChallenge = await oauthHandlerWithoutClientSecret.generateCodeChallenge(codeVerifier);
211+
212+
// Ensure the codeChallenge is a URL-safe Base64 string
213+
expect(codeChallenge).to.match(/^[A-Za-z0-9-_]+$/); // URL-safe Base64
214+
215+
// Clean up after the test to avoid affecting other tests
216+
delete global.window;
217+
});
218+
219+
it('should generate authorization URL with code_challenge when clientSecret is not provided', async () => {
220+
// Mock OAuthHandler without clientSecret
221+
oauthHandler = new OAuthHandler(
222+
axiosInstance,
223+
'appId',
224+
'clientId',
225+
'http://localhost:8184',
226+
null, // No clientSecret (PKCE)
227+
);
228+
229+
// Stub the generateCodeChallenge to return a dummy value
230+
const codeChallenge = 'dummyCodeChallenge';
231+
sandbox.stub(oauthHandler, 'generateCodeChallenge').resolves(codeChallenge);
232+
233+
const authUrl = await oauthHandler.authorize();
234+
235+
// Check that code_challenge and code_challenge_method are included in the URL
236+
expect(authUrl).to.include('https://example.com/');
237+
expect(authUrl).to.include('response_type=code');
238+
expect(authUrl).to.include('client_id=clientId');
239+
expect(authUrl).to.include('code_challenge=dummyCodeChallenge');
240+
expect(authUrl).to.include('code_challenge_method=S256');
241+
});
242+
243+
// Test cases for getOauthAppAuthorization
244+
describe('getOauthAppAuthorization', () => {
245+
it('should return authorization_uid when authorizations exist for the current user', async () => {
246+
const mockResponse = {
247+
data: {
248+
data: [
249+
{
250+
user: { uid: 'currentUserUid' },
251+
authorization_uid: 'authorizationUid1',
252+
},
253+
],
254+
},
255+
};
256+
257+
sandbox.stub(axiosInstance, 'get').resolves(mockResponse);
258+
259+
oauthHandler.axiosInstance.oauth.userUID = 'currentUserUid';
260+
const authorizationUid = await oauthHandler.getOauthAppAuthorization();
261+
262+
expect(authorizationUid).to.equal('authorizationUid1');
263+
});
264+
265+
it('should throw an error when no authorizations found for the current user', async () => {
266+
const mockResponse = {
267+
data: {
268+
data: [
269+
{
270+
user: { uid: 'otherUserUid' },
271+
authorization_uid: 'authorizationUid2',
272+
},
273+
],
274+
},
275+
};
276+
277+
sandbox.stub(axiosInstance, 'get').resolves(mockResponse);
278+
279+
oauthHandler.axiosInstance.oauth.userUID = 'currentUserUid';
280+
281+
try {
282+
await oauthHandler.getOauthAppAuthorization();
283+
throw new Error('Expected error not thrown');
284+
} catch (error) {
285+
expect(error.message).to.equal('No authorizations found for current user!');
286+
}
287+
});
288+
289+
it('should throw an error when no authorizations found for the app', async () => {
290+
const mockResponse = { data: { data: [] } };
291+
292+
sandbox.stub(axiosInstance, 'get').resolves(mockResponse);
293+
294+
try {
295+
await oauthHandler.getOauthAppAuthorization();
296+
throw new Error('Expected error not thrown');
297+
} catch (error) {
298+
expect(error.message).to.equal('No authorizations found for the app!');
299+
}
300+
});
301+
});
302+
303+
describe('revokeOauthAppAuthorization', () => {
304+
it('should make a DELETE request to revoke authorization when valid authorizationId is provided', async () => {
305+
const authorizationId = 'authorizationUid1';
306+
const mockResponse = { data: { success: true } };
307+
308+
sandbox.stub(axiosInstance, 'delete').resolves(mockResponse);
309+
310+
const result = await oauthHandler.revokeOauthAppAuthorization(authorizationId);
311+
312+
expect(result.success).to.be.true;
313+
expect(axiosInstance.delete.calledOnce).to.be.true;
314+
});
315+
316+
it('should not make a DELETE request when authorizationId is invalid or empty', async () => {
317+
const invalidAuthorizationId = '';
318+
const deleteStub = sandbox.stub(axiosInstance, 'delete');
319+
320+
await oauthHandler.revokeOauthAppAuthorization(invalidAuthorizationId);
321+
322+
expect(deleteStub.called).to.be.false;
323+
});
324+
});
325+
});

0 commit comments

Comments
 (0)