@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
JavaScript
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