@elastic.io/maester-client
Version:
The official object-storage client
229 lines (228 loc) • 12.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable no-unused-expressions */
const nock_1 = __importDefault(require("nock"));
const sinon_1 = __importDefault(require("sinon"));
const chai_1 = require("chai");
const get_stream_1 = __importDefault(require("get-stream"));
const jsonwebtoken_1 = require("jsonwebtoken");
const stream_1 = require("stream");
const StorageClient_1 = require("../src/StorageClient");
const errors_1 = require("../src/errors");
const logger_1 = __importDefault(require("../src/logger"));
const helpers_1 = require("./helpers");
const interfaces_1 = require("../src/interfaces");
describe('Storage Client', () => {
const config = {
uri: 'https://ma.es.ter',
jwtSecret: 'jwt',
userAgent: 'userAgent'
};
const storageClient = new StorageClient_1.StorageClient(config);
const data = { test: 'test' };
const responseData = {
contentLength: 'meta.contentLength',
contentType: 'meta.contentType',
createdAt: 'meta.createdAt',
md5: 'meta.md5Hash',
objectId: 'obj.id',
metadata: 'meta.userMetadata'
};
afterEach(() => {
sinon_1.default.restore();
});
function authHeaderMatch(jwtPayload) {
return (val) => {
const decoded = (0, jsonwebtoken_1.verify)(val.split(' ')[1], config.jwtSecret);
if (jwtPayload) {
(0, chai_1.expect)(decoded).to.deep.include(jwtPayload);
}
return !!decoded;
};
}
describe('get', () => {
it('should throw exception if neither jwt secret, nor jwt token provided', async () => {
const storageClient2 = new StorageClient_1.StorageClient({ uri: config.uri, userAgent: 'userAgent' });
let err;
try {
await storageClient2.get('1', { jwtPayloadOrToken: {} });
}
catch (e) {
err = e;
}
(0, chai_1.expect)(err.toString()).to.include('JWT');
});
it(`should fail after ${interfaces_1.RETRIES_COUNT.defaultValue} retries`, async () => {
const log = sinon_1.default.stub(logger_1.default, 'warn');
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.get('/objects/1')
.replyWithError({ code: 'ETIMEDOUT' })
.get('/objects/1')
.reply(502)
.get('/objects/1')
.replyWithError({ code: 'ENOTFOUND' });
let err;
try {
await storageClient.get('1', { jwtPayloadOrToken: {} });
}
catch (e) {
err = e;
}
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(err).to.be.instanceOf(errors_1.ServerTransportError);
(0, chai_1.expect)(log.getCall(1).args[1].toString()).to.include('Error during object request');
(0, chai_1.expect)(log.callCount).to.be.equal(interfaces_1.RETRIES_COUNT.defaultValue);
});
it('should not retry because of config', async () => {
const log = sinon_1.default.stub(logger_1.default, 'warn');
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.get('/objects/1')
.reply(500);
let err;
try {
await storageClient.get('1', { jwtPayloadOrToken: {}, retryOptions: { retriesCount: 0 } });
}
catch (e) {
err = e;
}
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(err).to.be.instanceOf(errors_1.ServerTransportError);
(0, chai_1.expect)(log.callCount).to.be.equal(0);
});
it('should not retry 4xx client error', async () => {
const log = sinon_1.default.stub(logger_1.default, 'warn');
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.get('/objects/1')
.reply(404);
await (0, chai_1.expect)(storageClient.get('1', { jwtPayloadOrToken: {} })).to.be.rejectedWith('Request failed with status code 404');
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(log.callCount).to.be.equal(0);
});
it('should accept jwt token on get', async () => {
const storageClient2 = new StorageClient_1.StorageClient({ uri: config.uri, userAgent: 'userAgent' });
const jwtPayload = { tenantId: '12', contractId: '1' };
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch(jwtPayload))
.get('/objects/1')
.reply(200, (0, helpers_1.streamFromObject)(data));
const response = await storageClient2.get('1', {
jwtPayloadOrToken: (0, jsonwebtoken_1.sign)(jwtPayload, config.jwtSecret)
});
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(response.data).to.be.instanceOf(stream_1.Readable);
const resultData = JSON.parse(await (0, get_stream_1.default)(response.data));
(0, chai_1.expect)(resultData).to.be.deep.equal(data);
});
it(`should retry get request ${interfaces_1.RETRIES_COUNT.defaultValue} times on errors`, async () => {
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.get('/objects/1')
.reply(502)
.get('/objects/1')
.replyWithError({ code: 'ECONNRESET' })
.get('/objects/1')
.reply(200, (0, helpers_1.streamFromObject)(data));
const response = await storageClient.get('1', { jwtPayloadOrToken: {} });
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(response.data).to.be.instanceOf(stream_1.Readable);
const resultData = JSON.parse(await (0, get_stream_1.default)(response.data));
(0, chai_1.expect)(resultData).to.be.deep.equal(data);
});
describe('post', () => {
it(`should retry post request ${interfaces_1.RETRIES_COUNT.defaultValue} times on errors`, async () => {
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.post('/objects')
.replyWithError({ code: 'ECONNREFUSED' })
.post('/objects')
.reply(502)
.post('/objects')
.reply(200, responseData);
const postStream = async () => (0, helpers_1.streamFromObject)(data);
const response = await storageClient.post(postStream, { jwtPayloadOrToken: {} });
(0, chai_1.expect)(response.data).to.be.deep.equal(responseData);
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
});
it('should accept jwt token on add', async () => {
const storageClient2 = new StorageClient_1.StorageClient({ uri: config.uri, userAgent: 'userAgent' });
const jwtPayload = { tenantId: '12', contractId: '1' };
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch(jwtPayload))
.post('/objects')
.reply(200, responseData);
const postStream = async () => (0, helpers_1.streamFromObject)(data);
const response = await storageClient2.post(postStream, { jwtPayloadOrToken: (0, jsonwebtoken_1.sign)(jwtPayload, config.jwtSecret) });
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(response.data).to.be.deep.equal(responseData);
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
});
});
describe('put', () => {
it(`should fail after ${interfaces_1.RETRIES_COUNT.defaultValue} retries`, async () => {
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.put('/objects/1')
.replyWithError({ code: 'ECONNREFUSED' })
.put('/objects/1')
.reply(502)
.put('/objects/1')
.replyWithError({ code: 'ENOTFOUND' });
let err;
try {
const putStream = async () => (0, helpers_1.streamFromObject)(data);
await storageClient.put('1', putStream, { jwtPayloadOrToken: {} });
}
catch (e) {
err = e;
}
(0, chai_1.expect)(err).to.be.instanceOf(errors_1.ServerTransportError);
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
});
it('should throw exception if neither jwt secret, nor jwt token provided', async () => {
const storageClient2 = new StorageClient_1.StorageClient({ uri: config.uri, userAgent: 'userAgent' });
let err;
try {
const putStream = async () => (0, helpers_1.streamFromObject)(data);
await storageClient2.put('1', putStream, { jwtPayloadOrToken: {} });
}
catch (e) {
err = e;
}
(0, chai_1.expect)(err).to.be.instanceOf(errors_1.JwtNotProvidedError);
});
it('should accept jwt token on add', async () => {
const storageClient2 = new StorageClient_1.StorageClient({ uri: config.uri, userAgent: 'userAgent' });
const jwtPayload = { tenantId: '12', contractId: '1' };
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch(jwtPayload))
.put('/objects/1')
.reply(200, responseData);
const putStream = async () => (0, helpers_1.streamFromObject)(data);
const response = await storageClient2.put('1', putStream, { jwtPayloadOrToken: (0, jsonwebtoken_1.sign)(jwtPayload, config.jwtSecret) });
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
(0, chai_1.expect)(response.data).to.be.deep.equal(responseData);
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
});
it('should retry post request 3 times on errors', async () => {
const storageClientCalls = (0, nock_1.default)(config.uri)
.matchHeader('authorization', authHeaderMatch())
.put('/objects/1')
.replyWithError({ code: 'ECONNREFUSED' })
.put('/objects/1')
.reply(502)
.put('/objects/1')
.reply(200, responseData);
const putStream = async () => (0, helpers_1.streamFromObject)(data);
const response = await storageClient.put('1', putStream, { jwtPayloadOrToken: {} });
(0, chai_1.expect)(response.data).to.be.deep.equal(responseData);
(0, chai_1.expect)(storageClientCalls.isDone()).to.be.true;
});
});
});
});