UNPKG

@microfocus/alm-octane-js-rest-sdk

Version:

NodeJS wrapper for the OpenText Core Software Delivery Platform API

527 lines (493 loc) 18.8 kB
/* * Copyright 2020-2025 Open Text. * * The only warranties for products and services of Open Text and * its affiliates and licensors (“Open Text”) are as may be set forth * in the express warranty statements accompanying such products and services. * Nothing herein should be construed as constituting an additional warranty. * Open Text shall not be liable for technical or editorial errors or * omissions contained herein. The information contained herein is subject * to change without notice. * * Except as specifically indicated otherwise, this document contains * confidential information and a valid license is required for possession, * use or copying. If this work is provided to the U.S. Government, * consistent with FAR 12.211 and 12.212, Commercial Computer Software, * Computer Software Documentation, and Technical Data for Commercial Items are * licensed to the U.S. Government under vendor's standard commercial license. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* eslint-env mocha */ import assert from 'assert'; import { AxiosInstance } from 'axios'; import MockAdapter from 'axios-mock-adapter'; import RequestHandler from '../../lib/root/requestHandler'; const testServerUrl = ''; const uri = '/some/uri'; const user = 'MyUser'; const password = 'MyPassword'; const UNAUTHORIZED_ERROR = { name: 'Error', response: { status: 401, }, }; describe('requestHandler', function () { // @ts-ignore this.slow(350); let requestHandler: RequestHandler; beforeEach(() => { const requestHandlerInitialization = { user: user, password: password, server: testServerUrl, sharedSpace: 1001, workspace: 1002, }; requestHandler = new RequestHandler(requestHandlerInitialization); }); async function throwsUnauthorizedException(promise: Promise<any>) { await throwsException(promise, 401, undefined); } async function throwsException( promise: Promise<any>, statusCode?: number, errorBody?: any ) { try { await promise; assert.fail(); } catch (e: any) { if (errorBody) { assert.strictEqual( JSON.stringify(e.response.data), JSON.stringify(errorBody) ); } if (statusCode) { assert.strictEqual(e.response.status, statusCode); } } } describe('#authenticate', () => { it('makes a successful authentication request with the given username and password', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withSuccessfulAuthentication(user, password) .build(); const shouldBeUndefined = await requestHandler.authenticate(); await assert.strictEqual(shouldBeUndefined.data, undefined); scope.reset(); }); it('throws an error after failed authentication', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException(requestHandler.authenticate()); scope.reset(); }); }); describe('#get', () => { it('throws an error in case of an error response different from 401', async () => { const error = { error: 'Failed as intended' }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulGetRequest(uri, error) .build(); await throwsException(requestHandler.get(uri), 400, error); scope.reset(); }); it('throws an error if request was answered with status code 401 two times', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedGetRequest(uri) .withSuccessfulAuthentication() .withUnauthorizedGetRequest(uri) .build(); await throwsUnauthorizedException(requestHandler.get(uri)); scope.reset(); }); it('throws an error if request was answered with status code 401 and authentication fails', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedGetRequest(uri) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException(requestHandler.get(uri)); scope.reset(); }); it('returns the data even if the 1st request was with the status code 401', async function () { const objectToGet = 'Request was successful'; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedGetRequest(uri) .withSuccessfulAuthentication() .withGetRequest(uri, 200, objectToGet) .build(); const objectGot = await requestHandler.get(uri); assert.strictEqual(objectGot.data, objectToGet); scope.reset(); }); it('makes a successful get request', async function () { const objectToGet = 'Request was successful'; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withGetRequest(uri, 200, objectToGet) .build(); const objectGot = await requestHandler.get(uri); assert.strictEqual(objectGot.data, objectToGet); scope.reset(); }); }); describe('#delete', () => { it('throws an error in case of an error response different from 401', async () => { const error = { error: 'Failed as intended' }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulDeleteRequest(uri, error) .build(); await throwsException(requestHandler.delete(uri), 400, error); scope.reset(); }); it('throws an error if request was answered with status code 401 two times', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedDeleteRequest(uri) .withSuccessfulAuthentication() .withUnauthorizedDeleteRequest(uri) .build(); await throwsUnauthorizedException(requestHandler.delete(uri)); scope.reset(); }); it('throws an error if request was answered with status code 401 and authentication fails', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedDeleteRequest(uri) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException(requestHandler.delete(uri)); scope.reset(); }); it('request is made even if 1st request was with the status code 401', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedDeleteRequest(uri) .withSuccessfulAuthentication() .withDeleteRequest(uri, 200) .build(); const shouldBeUndefined = await requestHandler.delete(uri); assert.strictEqual(shouldBeUndefined.data, undefined); scope.reset(); }); it('makes a successful delete request', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withDeleteRequest(uri, 200) .build(); const shouldBeUndefined = await requestHandler.delete(uri); assert.strictEqual(shouldBeUndefined.data, undefined); scope.reset(); }); }); describe('#update', () => { it('throws an error in case of an error response different from 401', async () => { const error = { error: 'Failed as intended' }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulUpdateRequest(uri, error) .build(); await throwsException(requestHandler.update(uri), 400, error); scope.reset(); }); it('throws an error if request was answered with status code 401 two times', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedUpdateRequest(uri) .withSuccessfulAuthentication() .withUnauthorizedUpdateRequest(uri) .build(); await throwsUnauthorizedException(requestHandler.update(uri)); scope.reset(); }); it('throws an error if request was answered with status code 401 and authentication fails', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedUpdateRequest(uri) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException(requestHandler.update(uri)); scope.reset(); }); it('request is made even if 1st request was with the status code 401', async function () { const updatedObject = 'Success'; const updateBody = { parameter1: 'para 1', parameter2: 2, }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedUpdateRequest(uri, updateBody, updatedObject) .withSuccessfulAuthentication() .withUpdateRequest(uri, 200, updateBody, updatedObject) .build(); const responseObject = await requestHandler.update(uri, updateBody); assert.strictEqual(responseObject.data, updatedObject); scope.reset(); }); it('makes a successful update request', async function () { const updatedObject = 'Success'; const updateBody = { parameter1: 'para 1', parameter2: 2, }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUpdateRequest(uri, 200, updateBody, updatedObject) .build(); const responseObject = await requestHandler.update(uri, updateBody); assert.strictEqual(responseObject.data, updatedObject); scope.reset(); }); }); describe('#create', () => { it('throws an error in case of an error response different from 401', async () => { const error = { error: 'Failed as intended' }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulCreateRequest(uri, error) .build(); await throwsException(requestHandler.create(uri), 400, error); scope.reset(); }); it('throws an error if request was answered with status code 401 two times', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedCreateRequest(uri) .withSuccessfulAuthentication() .withUnauthorizedCreateRequest(uri) .build(); await throwsUnauthorizedException(requestHandler.create(uri)); scope.reset(); }); it('throws an error if request was answered with status code 401 and authentication fails', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedCreateRequest(uri) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException(requestHandler.create(uri)); scope.reset(); }); it('request is made even if 1st request was with the status code 401', async function () { const createObject = 'Success'; const createBody = { parameter1: 'para 1', parameter2: 2, }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnauthorizedCreateRequest(uri, createBody, createObject) .withSuccessfulAuthentication() .withCreateRequest(uri, 200, createBody, createObject) .build(); const responseObject = await requestHandler.create(uri, createBody); assert.strictEqual(responseObject.data, createObject); scope.reset(); }); it('makes a successful update request', async function () { const createObject = { data: 'Success' }; const createBody = { parameter1: 'para 1', parameter2: 2, }; // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withCreateRequest(uri, 200, createBody, createObject) .build(); const responseObject = await requestHandler.create(uri, createBody); assert.strictEqual( JSON.stringify(responseObject.data), JSON.stringify(createObject) ); scope.reset(); }); }); describe('#reauthenicate', () => { it('throws the error given if it does not have the status code 401', async () => { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulAuthentication() .build(); const errorWithoutStatusCode = { response: { data: 'Error' } }; const errorWithStatusCode = { response: { status: 400, data: 'Error' } }; await throwsException( // @ts-ignore requestHandler._reauthenticate(errorWithoutStatusCode), undefined, errorWithoutStatusCode.response.data ); await throwsException( // @ts-ignore requestHandler._reauthenticate(errorWithStatusCode), 400, errorWithStatusCode.response.data ); scope.reset(); }); it('throws an error if the error received has status code 401 and the authentication fails ', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulAuthentication() .build(); await throwsUnauthorizedException( // @ts-ignore requestHandler._reauthenticate(UNAUTHORIZED_ERROR) ); scope.reset(); }); it('authenticates again if the received error has the status code 401', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withSuccessfulAuthentication() .build(); // @ts-ignore const shouldBeUndefined = await requestHandler._reauthenticate( UNAUTHORIZED_ERROR ); assert.strictEqual(shouldBeUndefined.data, undefined); scope.reset(); }); }); describe('#signOut', () => { it('throws an error if the request fails', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withUnsuccessfulSignOut() .build(); await throwsUnauthorizedException(requestHandler.signOut()); scope.reset(); }); it('throws an error if the request fails', async function () { // @ts-ignore const scope = new BuildServerResponses(requestHandler._requestor) .withSuccessfulSignOut() .build(); const shouldBeUndefined = await requestHandler.signOut(); assert.strictEqual(shouldBeUndefined.data, undefined); scope.reset(); }); }); class BuildServerResponses { scope: MockAdapter; authenticatedCookie: string; loginCookieName: string; constructor(requester: AxiosInstance) { this.scope = new MockAdapter(requester); this.authenticatedCookie = 'Authenticated'; this.loginCookieName = 'LWSSO_COOKIE_KEY'; } withSuccessfulAuthentication(user?: string, password?: string) { this.scope .onPost('/authentication/sign_in', { asymmetricMatch(actual: { user: string; password: string }) { if (user) { assert.strictEqual(actual.user, user); } if (password) { assert.strictEqual(actual.password, password); } return true; }, }) .reply(200, undefined, { 'Set-Cookie': this.loginCookieName + '=' + this.authenticatedCookie, }); return this; } withUnsuccessfulAuthentication() { this.scope .onPost('/authentication/sign_in') .reply(401, undefined, { 'Set-Cookie': this.loginCookieName + '=' }); return this; } withUnsuccessfulGetRequest(uri: string, errorBody: object) { this.scope.onGet(uri).reply(400, errorBody); return this; } withUnsuccessfulDeleteRequest(uri: string, errorBody: object) { this.scope.onDelete(uri).reply(400, errorBody); return this; } withUnsuccessfulUpdateRequest(uri: string, errorBody: object) { this.scope.onPut(uri).reply(400, errorBody); return this; } withUnsuccessfulCreateRequest(uri: string, errorBody: object) { this.scope.onPost(uri).reply(400, errorBody); return this; } withGetRequest(uri: string, statusCode: number, replyData?: any) { this.scope.onGet(uri).reply(statusCode, replyData); return this; } withDeleteRequest(uri: string, statusCode: number) { this.scope.onDelete(uri).reply(statusCode); return this; } withUpdateRequest( uri: string, statusCode: number, body?: object, replyData?: any ) { this.scope.onPut(uri, body).reply(statusCode, replyData); return this; } withCreateRequest( uri: string, statusCode: number, body?: object, replyData?: any ) { this.scope.onPost(uri, body).reply(statusCode, replyData); return this; } withUnauthorizedUpdateRequest(uri: string, body?: object, replyData?: any) { return this.withUpdateRequest(uri, 401, body, replyData); } withUnauthorizedGetRequest(uri: string) { return this.withGetRequest(uri, 401); } withUnauthorizedDeleteRequest(uri: string) { return this.withDeleteRequest(uri, 401); } withUnauthorizedCreateRequest(uri: string, body?: object, replyData?: any) { return this.withCreateRequest(uri, 401, body, replyData); } withSuccessfulSignOut() { this.scope .onPost('/authentication/sign_out') .reply(200, undefined, { 'Set-Cookie': this.loginCookieName + '=' }); return this; } withUnsuccessfulSignOut() { this.scope .onPost('/authentication/sign_out') .reply(401, undefined, { 'Set-Cookie': 'LWSSO_COOKIE_KEY=' }); return this; } build() { return this.scope; } } });