@microfocus/alm-octane-js-rest-sdk
Version:
NodeJS wrapper for the OpenText Core Software Delivery Platform API
467 lines (466 loc) • 20.9 kB
JavaScript
;
/*
* 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.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-env mocha */
const assert_1 = __importDefault(require("assert"));
const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
const requestHandler_1 = __importDefault(require("../../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;
beforeEach(() => {
const requestHandlerInitialization = {
user: user,
password: password,
server: testServerUrl,
sharedSpace: 1001,
workspace: 1002,
};
requestHandler = new requestHandler_1.default(requestHandlerInitialization);
});
async function throwsUnauthorizedException(promise) {
await throwsException(promise, 401, undefined);
}
async function throwsException(promise, statusCode, errorBody) {
try {
await promise;
assert_1.default.fail();
}
catch (e) {
if (errorBody) {
assert_1.default.strictEqual(JSON.stringify(e.response.data), JSON.stringify(errorBody));
}
if (statusCode) {
assert_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.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_1.default.strictEqual(shouldBeUndefined.data, undefined);
scope.reset();
});
});
class BuildServerResponses {
constructor(requester) {
this.scope = new axios_mock_adapter_1.default(requester);
this.authenticatedCookie = 'Authenticated';
this.loginCookieName = 'LWSSO_COOKIE_KEY';
}
withSuccessfulAuthentication(user, password) {
this.scope
.onPost('/authentication/sign_in', {
asymmetricMatch(actual) {
if (user) {
assert_1.default.strictEqual(actual.user, user);
}
if (password) {
assert_1.default.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, errorBody) {
this.scope.onGet(uri).reply(400, errorBody);
return this;
}
withUnsuccessfulDeleteRequest(uri, errorBody) {
this.scope.onDelete(uri).reply(400, errorBody);
return this;
}
withUnsuccessfulUpdateRequest(uri, errorBody) {
this.scope.onPut(uri).reply(400, errorBody);
return this;
}
withUnsuccessfulCreateRequest(uri, errorBody) {
this.scope.onPost(uri).reply(400, errorBody);
return this;
}
withGetRequest(uri, statusCode, replyData) {
this.scope.onGet(uri).reply(statusCode, replyData);
return this;
}
withDeleteRequest(uri, statusCode) {
this.scope.onDelete(uri).reply(statusCode);
return this;
}
withUpdateRequest(uri, statusCode, body, replyData) {
this.scope.onPut(uri, body).reply(statusCode, replyData);
return this;
}
withCreateRequest(uri, statusCode, body, replyData) {
this.scope.onPost(uri, body).reply(statusCode, replyData);
return this;
}
withUnauthorizedUpdateRequest(uri, body, replyData) {
return this.withUpdateRequest(uri, 401, body, replyData);
}
withUnauthorizedGetRequest(uri) {
return this.withGetRequest(uri, 401);
}
withUnauthorizedDeleteRequest(uri) {
return this.withDeleteRequest(uri, 401);
}
withUnauthorizedCreateRequest(uri, body, replyData) {
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;
}
}
});