UNPKG

@ikigai-gfc/tiny-storage-client

Version:

Tiny node client to request distributed AWS S3 or the OpenStack Swift Object Storage.

1,196 lines (1,030 loc) 275 kB
const storageSDK = require('../swift.js'); const nock = require('nock'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); const authURL = 'https://auth.cloud.ovh.net/v3'; const publicUrlGRA = 'https://storage.gra.cloud.ovh.net/v1/AUTH_ce3e510224d740a685cb0ae7bdb8ebc3'; const publicUrlSBG = 'https://storage.sbg.cloud.ovh.net/v1/AUTH_ce3e510224d740a685cb0ae7bdb8ebc3'; const tokenAuth = 'gAAAAABe8JlEGYPUwwOyjqgUBl11gSjDOw5VTtUZ5n8SWxghRGwakDkP_lelLfRctzyhbIFUXjsdPaGmV2xicL-9333lJUnL3M4JYlYCYMWsX3IhnLPYboyti835VdhAHQ7K_d0OC4OYvM04bvL3w_uSbkxPmL27uO0ISUgQdB_mHxoYlol8xYI' describe('Ovh Object Storage High Availability Node Client', function () { let storage = storageSDK(); beforeEach(function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.setTimeout(5000); storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }]); storage.connection((err) => { assert.strictEqual(err, null) assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }) }) describe('Connection', function () { it('should connect to object file storage', function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[20].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if status code is not a 200', function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(300, {}); storage.connection((err) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if endpoints cannot be found', function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutObjectStore); storage.connection((err) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if endpoints cannot be found because region is invalid', function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutRegion); storage.connection((err) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should connect to the second object storage if the first one is not available (error 500)', function (done) { storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[4].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should connect to the second object storage if any request is return', function (done) { storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .replyWithError("This is an error") .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[4].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should connect to the second object storage if the main storage timeout', function (done) { storage.setTimeout(200); storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .delayConnection(500) .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[4].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should connect to the second object storage if endpoints of the first object storage cannot be found', function (done) { storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutObjectStore) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[4].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should connect to the second object storage if endpoints of the first object storage cannot be found because region is invalid', function (done) { storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutRegion) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[4].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should reconnect to the first object storage if the first and second one are not available (error 500) but the first storage revive', function (done) { storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]) const firstNock = nock(authURL) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().activeStorage, 0); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[20].url); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); }); }); describe('log', function () { it('should overload the log function', function (done) { let i = 0; storage.setLogFunction(function (message, level) { assert.strictEqual(message.length > 0, true) assert.strictEqual(level.length > 0, true) i++; }) const firstMock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.connection((err) => { assert.strictEqual(err, null); assert.deepStrictEqual(storage.getConfig().token, tokenAuth); assert.deepStrictEqual(storage.getConfig().endpoints.url, connectionResultSuccessV3.token.catalog[9].endpoints[20].url); assert.strictEqual(i > 0, true); assert.strictEqual(firstMock.pendingMocks().length, 0); done(); }); }); }); describe('setStorage/getStorages/setTimeout/getConfig', function () { it('should update the initial configuration', function (done) { const _expectedConfig = { authUrl : 'https://carbone.io', username : 'John', password : 'Wick', tenantName : 'toto', region : 'GRA22' } storage.setStorages(_expectedConfig) const _storages = storage.getStorages(); assert.strictEqual(_storages[0].authUrl, _expectedConfig.authUrl); assert.strictEqual(_storages[0].username, _expectedConfig.username); assert.strictEqual(_storages[0].password, _expectedConfig.password); assert.strictEqual(_storages[0].tenantName, _expectedConfig.tenantName); assert.strictEqual(_storages[0].region, _expectedConfig.region); done(); }); it('should set the request timeout', function (done) { storage.setTimeout(200); assert.strictEqual(storage.getConfig().timeout, 200); done(); }); }); describe('listFiles', function() { describe("SINGLE STORAGE", function () { it('should return a list of files as a JSON and as an XML', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(_files[0].last_modified.length > 0, true) assert.strictEqual(_files[0].hash.length > 0, true) assert.strictEqual(_files[0].name.length > 0, true) assert.strictEqual(_files[0].content_type.length > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return a list of files as a XML and the header is overwritted', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.xml')); }); storage.listFiles('templates', { headers : { Accept: 'application/xml' } }, (err, body) => { assert.strictEqual(err, null); const _files = body.toString(); assert.strictEqual(_files.includes('<?xml'), true) assert.strictEqual(_files.includes('<container name="templates">'), true) assert.strictEqual(_files.includes('<bytes>47560</bytes>'), true) assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }) it('should return a list of files with a prefix', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .query({ prefix : 'keys' }) .reply(200, () => { let _file = fs.readFileSync(path.join(__dirname, 'assets', 'files.json')) return Buffer.from(JSON.stringify(JSON.parse(_file.toString()).filter((el => el.name.includes('keys'))))); }); storage.listFiles('templates', { queries: { prefix: 'keys' } }, (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); _files.forEach(el => { assert.strictEqual(el.name.includes('keys'), true); }); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should reconnect automatically to the storage', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(401, 'Unauthorized') .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(_files[0].last_modified.length > 0, true) assert.strictEqual(_files[0].hash.length > 0, true) assert.strictEqual(_files[0].name.length > 0, true) assert.strictEqual(_files[0].content_type.length > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); done(); }); }); it('should reconnect automatically to the storage with a prefix and delimiter as option/query parameters', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .query({ prefix : 'keys', delimiter : '/' }) .reply(401, 'Unauthorized') .get('/templates') .query({ prefix : 'keys', delimiter : '/' }) .reply(200, () => { return Buffer.from(JSON.stringify([ { "subdir": "keys/" }])); }); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.listFiles('templates', { queries: { prefix: 'keys', delimiter : '/' } }, (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files[0].subdir, 'keys/') assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); done(); }); }); it('should return an error if the single storage timeout', function (done) { storage.setTimeout(200); const firstNock = nock(publicUrlGRA) .get('/templates') .delayConnection(500) .reply(200, {}) storage.listFiles('templates', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if the single storage return any kind of errors', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .replyWithError('Error Message 1234') storage.listFiles('templates', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.deepStrictEqual(storage.getConfig().activeStorage, 0); done(); }); }); it('should return an error if the container does not exist', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(404, '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'); storage.listFiles('templates', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Container does not exist'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); }); describe("MUTLIPLE STORAGES", function () { beforeEach(function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.setTimeout(5000); storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]); storage.connection((err) => { assert.strictEqual(err, null) assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }) }) it('should reconnect automatically to the second object storage if the first storage authentication fail and should retry the request', function (done) { let firstNock = nock(publicUrlGRA) /** 1 */ .get('/templates') .reply(401, 'Unauthorized') let secondNock = nock(authURL) /** 2 */ .post('/auth/tokens') .reply(500, {}) /** 3 */ .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) /** 4 */ .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(_files[0].last_modified.length > 0, true) assert.strictEqual(_files[0].hash.length > 0, true) assert.strictEqual(_files[0].name.length > 0, true) assert.strictEqual(_files[0].content_type.length > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); done(); }); }); it('should retry the request with the second object storage if the first object storage return a 500 error', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(500, {}); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(_files[0].last_modified.length > 0, true) assert.strictEqual(_files[0].hash.length > 0, true) assert.strictEqual(_files[0].name.length > 0, true) assert.strictEqual(_files[0].content_type.length > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); done(); }); }); it('should retry the request with the second object storage if the first object storage timeout', function (done) { storage.setTimeout(200); let firstNock = nock(publicUrlGRA) .get('/templates') .delayConnection(500) .reply(200, {}); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(_files[0].last_modified.length > 0, true) assert.strictEqual(_files[0].hash.length > 0, true) assert.strictEqual(_files[0].name.length > 0, true) assert.strictEqual(_files[0].content_type.length > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); done(); }); }); it('should retry the request with the second storage if the first storage return any kind of errors', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .replyWithError('Error Message 1234'); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); storage.listFiles('templates', (err, body) => { assert.strictEqual(err, null); const _files = JSON.parse(body.toString()); assert.strictEqual(_files.length > 0, true) assert.strictEqual(_files[0].bytes > 0, true) assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); assert.deepStrictEqual(storage.getConfig().activeStorage, 1); done(); }); }); describe("PARALLEL REQUESTS", function () { function getListFilesPromise() { return new Promise((resolve, reject) => { try { storage.listFiles('templates', (err, body) => { if (err) { return reject(err); } return resolve(body.toString()); }); } catch(err) { return reject(err); } }); } it('should request the object storage in parallel and fallback to SBG if the main storage return any kind of errors', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .replyWithError('Error Message 1234') .get('/templates') .replyWithError('Error Message 1234'); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); let promise1 = getListFilesPromise() let promise2 = getListFilesPromise() Promise.all([promise1, promise2]).then(results => { assert.strictEqual(results.length, 2) const _listFiles1 = JSON.parse(results[0].toString()); assert.strictEqual(_listFiles1.length > 0, true) assert.strictEqual(_listFiles1[0].bytes > 0, true) assert.strictEqual(_listFiles1[0].last_modified.length > 0, true) assert.strictEqual(_listFiles1[0].hash.length > 0, true) assert.strictEqual(_listFiles1[0].name.length > 0, true) assert.strictEqual(_listFiles1[0].content_type.length > 0, true) const _listFiles2 = JSON.parse(results[1].toString()); assert.strictEqual(_listFiles2.length > 0, true) assert.strictEqual(_listFiles2[0].bytes > 0, true) assert.strictEqual(_listFiles2[0].last_modified.length > 0, true) assert.strictEqual(_listFiles2[0].hash.length > 0, true) assert.strictEqual(_listFiles2[0].name.length > 0, true) assert.strictEqual(_listFiles2[0].content_type.length > 0, true) assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); done(); }).catch(err => { assert.strictEqual(err, null); done(); }); }); it('should request the object storage in parallel and fallback to SBG if the authentication of the main storage return an error', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(401, 'Unauthorized') .get('/templates') .reply(401, 'Unauthorized') .get('/templates') .reply(401, 'Unauthorized') let secondNock = nock(authURL) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(500, {}) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); let promise1 = getListFilesPromise() let promise2 = getListFilesPromise() let promise3 = getListFilesPromise() Promise.all([promise1, promise2, promise3]).then(async results => { assert.strictEqual(results.length, 3) const _listFiles1 = JSON.parse(results[0].toString()); assert.strictEqual(_listFiles1.length > 0, true) assert.strictEqual(_listFiles1[0].bytes > 0, true) assert.strictEqual(_listFiles1[0].last_modified.length > 0, true) assert.strictEqual(_listFiles1[0].hash.length > 0, true) assert.strictEqual(_listFiles1[0].name.length > 0, true) assert.strictEqual(_listFiles1[0].content_type.length > 0, true) const _listFiles2 = JSON.parse(results[1].toString()); assert.strictEqual(_listFiles2.length > 0, true) assert.strictEqual(_listFiles2[0].bytes > 0, true) assert.strictEqual(_listFiles2[0].last_modified.length > 0, true) assert.strictEqual(_listFiles2[0].hash.length > 0, true) assert.strictEqual(_listFiles2[0].name.length > 0, true) assert.strictEqual(_listFiles2[0].content_type.length > 0, true) const _listFiles3 = JSON.parse(results[2].toString()); assert.strictEqual(_listFiles3.length > 0, true) assert.strictEqual(_listFiles3[0].bytes > 0, true) assert.strictEqual(_listFiles3[0].last_modified.length > 0, true) assert.strictEqual(_listFiles3[0].hash.length > 0, true) assert.strictEqual(_listFiles3[0].name.length > 0, true) assert.strictEqual(_listFiles3[0].content_type.length > 0, true) let _listFiles4 = JSON.parse((await getListFilesPromise()).toString()); assert.strictEqual(_listFiles4.length > 0, true) assert.strictEqual(_listFiles4[0].bytes > 0, true) assert.strictEqual(_listFiles4[0].last_modified.length > 0, true) assert.strictEqual(_listFiles4[0].hash.length > 0, true) assert.strictEqual(_listFiles4[0].name.length > 0, true) assert.strictEqual(_listFiles4[0].content_type.length > 0, true) assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); done(); }); }); it('should request the object storage in parallel and fallback to SBG if the main storage timeout', function (done) { storage.setTimeout(200); let firstNock = nock(publicUrlGRA) .get('/templates') .delayConnection(500) .reply(200, {}) .get('/templates') .delayConnection(500) .reply(200, {}); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); let promise1 = getListFilesPromise() let promise2 = getListFilesPromise() Promise.all([promise1, promise2]).then(results => { assert.strictEqual(results.length, 2) const _listFiles1 = JSON.parse(results[0].toString()); assert.strictEqual(_listFiles1.length > 0, true) assert.strictEqual(_listFiles1[0].bytes > 0, true) assert.strictEqual(_listFiles1[0].last_modified.length > 0, true) assert.strictEqual(_listFiles1[0].hash.length > 0, true) assert.strictEqual(_listFiles1[0].name.length > 0, true) assert.strictEqual(_listFiles1[0].content_type.length > 0, true) const _listFiles2 = JSON.parse(results[1].toString()); assert.strictEqual(_listFiles2.length > 0, true) assert.strictEqual(_listFiles2[0].bytes > 0, true) assert.strictEqual(_listFiles2[0].last_modified.length > 0, true) assert.strictEqual(_listFiles2[0].hash.length > 0, true) assert.strictEqual(_listFiles2[0].name.length > 0, true) assert.strictEqual(_listFiles2[0].content_type.length > 0, true) assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); done(); }).catch(err => { assert.strictEqual(err, null); done(); }); }); it('should request the object storage in parallel and fallback to SBG if the main storage return a 500 error', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(500, {}) .get('/templates') .reply(500, {}); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }); let promise1 = getListFilesPromise() let promise2 = getListFilesPromise() Promise.all([promise1, promise2]).then(results => { assert.strictEqual(results.length, 2) const _listFiles1 = JSON.parse(results[0].toString()); assert.strictEqual(_listFiles1.length > 0, true) assert.strictEqual(_listFiles1[0].bytes > 0, true) assert.strictEqual(_listFiles1[0].last_modified.length > 0, true) assert.strictEqual(_listFiles1[0].hash.length > 0, true) assert.strictEqual(_listFiles1[0].name.length > 0, true) assert.strictEqual(_listFiles1[0].content_type.length > 0, true) const _listFiles2 = JSON.parse(results[1].toString()); assert.strictEqual(_listFiles2.length > 0, true) assert.strictEqual(_listFiles2[0].bytes > 0, true) assert.strictEqual(_listFiles2[0].last_modified.length > 0, true) assert.strictEqual(_listFiles2[0].hash.length > 0, true) assert.strictEqual(_listFiles2[0].name.length > 0, true) assert.strictEqual(_listFiles2[0].content_type.length > 0, true) assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); done(); }).catch(err => { assert.strictEqual(err, null); done(); }); }); it('should request the object storage in parallel and fallback to SBG if the authentication of the main storage return does not return the object storage list [Special case and should never happen]', function (done) { let firstNock = nock(publicUrlGRA) .get('/templates') .reply(401, 'Unauthorized') .get('/templates') .reply(401, 'Unauthorized') let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutObjectStore) .post('/auth/tokens') .reply(200, connectionResultSuccessV3WithoutObjectStore) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }) let thirdNock = nock(publicUrlSBG) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) .get('/templates') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'files.json')); }) let promise1 = getListFilesPromise() let promise2 = getListFilesPromise() Promise.all([promise1, promise2]).then(async results => { assert.strictEqual(results.length, 2) const _listFiles1 = JSON.parse(results[0].toString()); assert.strictEqual(_listFiles1.length > 0, true) assert.strictEqual(_listFiles1[0].bytes > 0, true) assert.strictEqual(_listFiles1[0].last_modified.length > 0, true) assert.strictEqual(_listFiles1[0].hash.length > 0, true) assert.strictEqual(_listFiles1[0].name.length > 0, true) assert.strictEqual(_listFiles1[0].content_type.length > 0, true) const _listFiles2 = JSON.parse(results[1].toString()); assert.strictEqual(_listFiles2.length > 0, true) assert.strictEqual(_listFiles2[0].bytes > 0, true) assert.strictEqual(_listFiles2[0].last_modified.length > 0, true) assert.strictEqual(_listFiles2[0].hash.length > 0, true) assert.strictEqual(_listFiles2[0].name.length > 0, true) assert.strictEqual(_listFiles2[0].content_type.length > 0, true) assert.deepStrictEqual(storage.getConfig().activeStorage, 1); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(thirdNock.pendingMocks().length, 0); done(); }); }); }); }); }); describe('downloadFile', function () { describe('SINGLE STORAGE', function () { it('should download file and return the header', function (done) { const firstNock = nock(publicUrlGRA) .defaultReplyHeaders({ 'content-length': '1492', 'accept-ranges': 'bytes', 'last-modified': 'Wed, 03 Nov 2021 13:02:39 GMT', 'content-type': 'application/json', etag: 'a30776a059eaf26eebf27756a849097d', 'x-openstack-request-id': 'tx136c028c478a4b40a7014-0061829c9f', date: 'Wed, 03 Nov 2021 14:28:48 GMT', 'x-iplb-request-id': '25A66014:1D97_3626E64B:01BB_61829C9E_3C28BD:960D' }) .get('/templates/test.odt') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'file.txt')); }); storage.downloadFile('templates', 'test.odt', (err, body, headers) => { assert.strictEqual(err, null); assert.strictEqual(body.toString(), 'The platypus, sometimes referred to as the duck-billed platypus, is a semiaquatic, egg-laying mammal endemic to eastern Australia.'); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(headers['etag'].length > 0, true); assert.strictEqual(headers['x-openstack-request-id'].length > 0, true); assert.strictEqual(headers['content-length'].length > 0, true); assert.strictEqual(headers['date'].length > 0, true); done(); }); }); it('should reconnect automatically to object storage and retry', function (done) { let firstNock = nock(publicUrlGRA) .defaultReplyHeaders({ 'content-length': '1492', 'accept-ranges': 'bytes', 'last-modified': 'Wed, 03 Nov 2021 13:02:39 GMT', 'content-type': 'application/json', etag: 'a30776a059eaf26eebf27756a849097d', 'x-openstack-request-id': 'tx136c028c478a4b40a7014-0061829c9f', date: 'Wed, 03 Nov 2021 14:28:48 GMT', 'x-iplb-request-id': '25A66014:1D97_3626E64B:01BB_61829C9E_3C28BD:960D' }) .get('/templates/test.odt') .reply(401, 'Unauthorized') .get('/templates/test.odt') .reply(200, () => { return fs.createReadStream(path.join(__dirname, 'assets', 'file.txt')); }); let secondNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.downloadFile('templates', 'test.odt', (err, body, headers) => { assert.strictEqual(err, null); assert.strictEqual(body.toString(), 'The platypus, sometimes referred to as the duck-billed platypus, is a semiaquatic, egg-laying mammal endemic to eastern Australia.'); assert.strictEqual(firstNock.pendingMocks().length, 0); assert.strictEqual(secondNock.pendingMocks().length, 0); assert.strictEqual(headers['etag'].length > 0, true); assert.strictEqual(headers['x-openstack-request-id'].length > 0, true); assert.strictEqual(headers['content-length'].length > 0, true); assert.strictEqual(headers['date'].length > 0, true); done(); }); }); it('should return an error if the single storage timout', function (done) { storage.setTimeout(200); const firstNock = nock(publicUrlGRA) .get('/templates/test.odt') .delayConnection(500) .reply(200, {}) storage.downloadFile('templates', 'test.odt', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if the single storage return any kind of errors', function (done) { const firstNock = nock(publicUrlGRA) .get('/templates/test.odt') .replyWithError('Error Message 1234'); storage.downloadFile('templates', 'test.odt', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'Object Storages are not available'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); it('should return an error if the file does not exist', function (done) { const firstNock = nock(publicUrlGRA) .get('/templates/test.odt') .reply(404); storage.downloadFile('templates', 'test.odt', (err, body) => { assert.notStrictEqual(err, null); assert.strictEqual(err.message, 'File does not exist'); assert.strictEqual(body, undefined); assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }); }); }); describe('MULTIPLE STORAGES', function () { beforeEach(function (done) { const firstNock = nock(authURL) .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); storage.setTimeout(5000); storage.setStorages([{ username : 'storage-1-user', password : 'storage-1-password', authUrl : authURL, tenantName : 'storage-1-tenant', region : 'GRA' }, { username : 'storage-2-user', password : 'storage-2-password', authUrl : authURL, tenantName : 'storage-2-tenant', region : 'SBG' }]); storage.connection((err) => { assert.strictEqual(err, null) assert.strictEqual(firstNock.pendingMocks().length, 0); done(); }) }) it('should reconnect automatically to the second object storage if the first storage authentication fail and should retry the request', function(done){ let firstNock = nock(publicUrlGRA) /** 1 */ .get('/templates/test.odt') .reply(401, 'Unauthorized'); let secondNock = nock(authURL) /** 2 */ .post('/auth/tokens') .reply(500, {}) /** 3 */ .post('/auth/tokens') .reply(200, connectionResultSuccessV3, { "X-Subject-Token": tokenAuth }); let thirdNock = nock(publicUrlSBG) /** 4 */ .defaultReplyHeaders({ 'content-length': '1492', 'accept-ranges': 'bytes', 'last-modified': 'Wed, 03 Nov 2021 13:02:39 GMT', 'content-type': 'application/json', etag: 'a30776a059eaf26eebf27756a849097d', 'x-openstack-reque