updated docs
This commit is contained in:
parent
cfc94fd9af
commit
18e1609cb3
26 changed files with 309 additions and 14 deletions
105
test/backend/auth-tests.js
Normal file
105
test/backend/auth-tests.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
const assert = require('assert');
|
||||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
metadata: sinon.stub(),
|
||||
setField: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id, auth) {
|
||||
return {
|
||||
params: { id },
|
||||
header: sinon.stub().returns(auth)
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub(),
|
||||
set: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const next = sinon.stub();
|
||||
|
||||
const storedMeta = {
|
||||
auth:
|
||||
'r9uFxEs9GEVaQR9CJJ0uTKFGhFSOTRjOY2FCLFlCIZ0Cr-VGTVpMGlXDbNR8RMT55trMpSrzWtBVKq1LffOT2g',
|
||||
nonce: 'FL4oxA7IE1PW8shwFN9qZw=='
|
||||
};
|
||||
|
||||
const authMiddleware = proxyquire('../../server/middleware/auth', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('Owner Middleware', function() {
|
||||
afterEach(function() {
|
||||
storage.metadata.reset();
|
||||
storage.setField.reset();
|
||||
next.reset();
|
||||
});
|
||||
|
||||
it('sends a 401 when no auth header is set', async function() {
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
await authMiddleware(req, res, next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
sinon.assert.notCalled(next);
|
||||
});
|
||||
|
||||
it('sends a 404 when metadata is not found', async function() {
|
||||
const req = request('x', 'y');
|
||||
const res = response();
|
||||
await authMiddleware(req, res, next);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
sinon.assert.notCalled(next);
|
||||
});
|
||||
|
||||
it('sends a 401 when the auth header is invalid base64', async function() {
|
||||
storage.metadata.returns(Promise.resolve(storedMeta));
|
||||
const req = request('x', '1');
|
||||
const res = response();
|
||||
await authMiddleware(req, res, next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
sinon.assert.notCalled(next);
|
||||
});
|
||||
|
||||
it('authenticates when the hashes match', async function() {
|
||||
storage.metadata.returns(Promise.resolve(storedMeta));
|
||||
const req = request(
|
||||
'x',
|
||||
'send-v1 R7nZk14qJqZXtxpnAtw2uDIRQTRnO1qSO1Q0PiwcNA8'
|
||||
);
|
||||
const res = response();
|
||||
await authMiddleware(req, res, next);
|
||||
sinon.assert.calledOnce(next);
|
||||
sinon.assert.calledWith(storage.setField, 'x', 'nonce', req.nonce);
|
||||
sinon.assert.calledWith(
|
||||
res.set,
|
||||
'WWW-Authenticate',
|
||||
`send-v1 ${req.nonce}`
|
||||
);
|
||||
sinon.assert.notCalled(res.sendStatus);
|
||||
assert.equal(req.authorized, true);
|
||||
assert.equal(req.meta, storedMeta);
|
||||
assert.notEqual(req.nonce, storedMeta.nonce);
|
||||
});
|
||||
|
||||
it('sends a 401 when the hashes do not match', async function() {
|
||||
storage.metadata.returns(Promise.resolve(storedMeta));
|
||||
const req = request(
|
||||
'x',
|
||||
'send-v1 R8nZk14qJqZXtxpnAtw2uDIRQTRnO1qSO1Q0PiwcNA8'
|
||||
);
|
||||
const res = response();
|
||||
await authMiddleware(req, res, next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
sinon.assert.calledWith(
|
||||
res.set,
|
||||
'WWW-Authenticate',
|
||||
`send-v1 ${storedMeta.nonce}`
|
||||
);
|
||||
sinon.assert.notCalled(next);
|
||||
});
|
||||
});
|
43
test/backend/delete-tests.js
Normal file
43
test/backend/delete-tests.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
del: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id) {
|
||||
return {
|
||||
params: { id }
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const delRoute = proxyquire('../../server/routes/delete', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('/api/delete', function() {
|
||||
afterEach(function() {
|
||||
storage.del.reset();
|
||||
});
|
||||
|
||||
it('calls storage.del with the id parameter', async function() {
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
await delRoute(req, res);
|
||||
sinon.assert.calledWith(storage.del, 'x');
|
||||
sinon.assert.calledWith(res.sendStatus, 200);
|
||||
});
|
||||
|
||||
it('sends a 404 on failure', async function() {
|
||||
storage.del.returns(Promise.reject(new Error()));
|
||||
const res = response();
|
||||
await delRoute(request('x'), res);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
});
|
59
test/backend/info-tests.js
Normal file
59
test/backend/info-tests.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
ttl: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id, meta) {
|
||||
return {
|
||||
params: { id },
|
||||
meta
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub(),
|
||||
send: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const infoRoute = proxyquire('../../server/routes/info', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('/api/info', function() {
|
||||
afterEach(function() {
|
||||
storage.ttl.reset();
|
||||
});
|
||||
|
||||
it('calls storage.ttl with the id parameter', async function() {
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
await infoRoute(req, res);
|
||||
sinon.assert.calledWith(storage.ttl, 'x');
|
||||
});
|
||||
|
||||
it('sends a 404 on failure', async function() {
|
||||
storage.ttl.returns(Promise.reject(new Error()));
|
||||
const res = response();
|
||||
await infoRoute(request('x'), res);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
|
||||
it('returns a json object', async function() {
|
||||
storage.ttl.returns(Promise.resolve(123));
|
||||
const meta = {
|
||||
dlimit: '1',
|
||||
dl: '0'
|
||||
};
|
||||
const res = response();
|
||||
await infoRoute(request('x', meta), res);
|
||||
sinon.assert.calledWithMatch(res.send, {
|
||||
dlimit: 1,
|
||||
dtotal: 0,
|
||||
ttl: 123
|
||||
});
|
||||
});
|
||||
});
|
67
test/backend/language-tests.js
Normal file
67
test/backend/language-tests.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const assert = require('assert');
|
||||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const config = {
|
||||
l10n_dev: false // prod configuration
|
||||
};
|
||||
const pkg = {
|
||||
availableLanguages: ['en-US', 'fr', 'it', 'es-ES']
|
||||
};
|
||||
|
||||
function request(acceptLang) {
|
||||
return {
|
||||
headers: {
|
||||
'accept-language': acceptLang
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const langMiddleware = proxyquire('../../server/middleware/language', {
|
||||
'../config': config,
|
||||
'../../package.json': pkg
|
||||
});
|
||||
|
||||
describe('Language Middleware', function() {
|
||||
it('defaults to en-US when no header is present', function() {
|
||||
const req = request();
|
||||
const next = sinon.stub();
|
||||
langMiddleware(req, null, next);
|
||||
assert.equal(req.language, 'en-US');
|
||||
sinon.assert.calledOnce(next);
|
||||
});
|
||||
|
||||
it('sets req.language to en-US when accept-language > 255 chars', function() {
|
||||
const accept = Array(257).join('a');
|
||||
assert.equal(accept.length, 256);
|
||||
const req = request(accept);
|
||||
const next = sinon.stub();
|
||||
langMiddleware(req, null, next);
|
||||
assert.equal(req.language, 'en-US');
|
||||
sinon.assert.calledOnce(next);
|
||||
});
|
||||
|
||||
it('defaults to en-US when no accept-language is available', function() {
|
||||
const req = request('fa,cs,ja');
|
||||
const next = sinon.stub();
|
||||
langMiddleware(req, null, next);
|
||||
assert.equal(req.language, 'en-US');
|
||||
sinon.assert.calledOnce(next);
|
||||
});
|
||||
|
||||
it('prefers higher q values', function() {
|
||||
const req = request('fa;q=0.5, it;q=0.9');
|
||||
const next = sinon.stub();
|
||||
langMiddleware(req, null, next);
|
||||
assert.equal(req.language, 'it');
|
||||
sinon.assert.calledOnce(next);
|
||||
});
|
||||
|
||||
it('uses likely subtags', function() {
|
||||
const req = request('es-MX');
|
||||
const next = sinon.stub();
|
||||
langMiddleware(req, null, next);
|
||||
assert.equal(req.language, 'es-ES');
|
||||
sinon.assert.calledOn(next);
|
||||
});
|
||||
});
|
65
test/backend/metadata-tests.js
Normal file
65
test/backend/metadata-tests.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
ttl: sinon.stub(),
|
||||
length: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id, meta) {
|
||||
return {
|
||||
params: { id },
|
||||
meta
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub(),
|
||||
send: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const metadataRoute = proxyquire('../../server/routes/metadata', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('/api/metadata', function() {
|
||||
afterEach(function() {
|
||||
storage.ttl.reset();
|
||||
storage.length.reset();
|
||||
});
|
||||
|
||||
it('calls storage.[ttl|length] with the id parameter', async function() {
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
await metadataRoute(req, res);
|
||||
sinon.assert.calledWith(storage.ttl, 'x');
|
||||
sinon.assert.calledWith(storage.length, 'x');
|
||||
});
|
||||
|
||||
it('sends a 404 on failure', async function() {
|
||||
storage.length.returns(Promise.reject(new Error()));
|
||||
const res = response();
|
||||
await metadataRoute(request('x'), res);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
|
||||
it('returns a json object', async function() {
|
||||
storage.ttl.returns(Promise.resolve(123));
|
||||
storage.length.returns(Promise.resolve(987));
|
||||
const meta = {
|
||||
dlimit: 1,
|
||||
dl: 0,
|
||||
metadata: 'foo'
|
||||
};
|
||||
const res = response();
|
||||
await metadataRoute(request('x', meta), res);
|
||||
sinon.assert.calledWithMatch(res.send, {
|
||||
metadata: 'foo',
|
||||
finalDownload: true,
|
||||
size: 987,
|
||||
ttl: 123
|
||||
});
|
||||
});
|
||||
});
|
81
test/backend/owner-tests.js
Normal file
81
test/backend/owner-tests.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
const assert = require('assert');
|
||||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
metadata: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id, owner_token) {
|
||||
return {
|
||||
params: { id },
|
||||
body: { owner_token }
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const ownerMiddleware = proxyquire('../../server/middleware/owner', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('Owner Middleware', function() {
|
||||
afterEach(function() {
|
||||
storage.metadata.reset();
|
||||
});
|
||||
|
||||
it('sends a 404 when the id is not found', async function() {
|
||||
const next = sinon.stub();
|
||||
storage.metadata.returns(Promise.resolve(null));
|
||||
const res = response();
|
||||
await ownerMiddleware(request('x', 'y'), res);
|
||||
sinon.assert.notCalled(next);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
|
||||
it('sends a 401 when the owner_token is missing', async function() {
|
||||
const next = sinon.stub();
|
||||
const meta = { owner: 'y' };
|
||||
storage.metadata.returns(Promise.resolve(meta));
|
||||
const res = response();
|
||||
await ownerMiddleware(request('x', null), res);
|
||||
sinon.assert.notCalled(next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
});
|
||||
|
||||
it('sends a 401 when the owner_token does not match', async function() {
|
||||
const next = sinon.stub();
|
||||
const meta = { owner: 'y' };
|
||||
storage.metadata.returns(Promise.resolve(meta));
|
||||
const res = response();
|
||||
await ownerMiddleware(request('x', 'z'), res);
|
||||
sinon.assert.notCalled(next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
});
|
||||
|
||||
it('sends a 401 if the metadata call fails', async function() {
|
||||
const next = sinon.stub();
|
||||
storage.metadata.returns(Promise.reject(new Error()));
|
||||
const res = response();
|
||||
await ownerMiddleware(request('x', 'y'), res);
|
||||
sinon.assert.notCalled(next);
|
||||
sinon.assert.calledWith(res.sendStatus, 401);
|
||||
});
|
||||
|
||||
it('sets req.meta and req.authorized on successful auth', async function() {
|
||||
const next = sinon.stub();
|
||||
const meta = { owner: 'y' };
|
||||
storage.metadata.returns(Promise.resolve(meta));
|
||||
const req = request('x', 'y');
|
||||
const res = response();
|
||||
await ownerMiddleware(req, res, next);
|
||||
assert.equal(req.meta, meta);
|
||||
assert.equal(req.authorized, true);
|
||||
sinon.assert.notCalled(res.sendStatus);
|
||||
sinon.assert.calledOnce(next);
|
||||
});
|
||||
});
|
56
test/backend/params-tests.js
Normal file
56
test/backend/params-tests.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
setField: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id) {
|
||||
return {
|
||||
params: { id },
|
||||
body: {}
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const paramsRoute = proxyquire('../../server/routes/params', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('/api/params', function() {
|
||||
afterEach(function() {
|
||||
storage.setField.reset();
|
||||
});
|
||||
|
||||
it('calls storage.setField with the correct parameter', function() {
|
||||
const req = request('x');
|
||||
const dlimit = 2;
|
||||
req.body.dlimit = dlimit;
|
||||
const res = response();
|
||||
paramsRoute(req, res);
|
||||
sinon.assert.calledWith(storage.setField, 'x', 'dlimit', dlimit);
|
||||
sinon.assert.calledWith(res.sendStatus, 200);
|
||||
});
|
||||
|
||||
it('sends a 400 if dlimit is too large', function() {
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
req.body.dlimit = 21;
|
||||
paramsRoute(req, res);
|
||||
sinon.assert.calledWith(res.sendStatus, 400);
|
||||
});
|
||||
|
||||
it('sends a 404 on failure', function() {
|
||||
storage.setField.throws(new Error());
|
||||
const req = request('x');
|
||||
const res = response();
|
||||
req.body.dlimit = 2;
|
||||
paramsRoute(req, res);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
});
|
53
test/backend/password-tests.js
Normal file
53
test/backend/password-tests.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const storage = {
|
||||
setField: sinon.stub()
|
||||
};
|
||||
|
||||
function request(id, body) {
|
||||
return {
|
||||
params: { id },
|
||||
body
|
||||
};
|
||||
}
|
||||
|
||||
function response() {
|
||||
return {
|
||||
sendStatus: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
const passwordRoute = proxyquire('../../server/routes/password', {
|
||||
'../storage': storage
|
||||
});
|
||||
|
||||
describe('/api/password', function() {
|
||||
afterEach(function() {
|
||||
storage.setField.reset();
|
||||
});
|
||||
|
||||
it('calls storage.setField with the correct parameter', function() {
|
||||
const req = request('x', { auth: 'z' });
|
||||
const res = response();
|
||||
passwordRoute(req, res);
|
||||
sinon.assert.calledWith(storage.setField, 'x', 'auth', 'z');
|
||||
sinon.assert.calledWith(storage.setField, 'x', 'pwd', true);
|
||||
sinon.assert.calledWith(res.sendStatus, 200);
|
||||
});
|
||||
|
||||
it('sends a 400 if auth is missing', function() {
|
||||
const req = request('x', {});
|
||||
const res = response();
|
||||
passwordRoute(req, res);
|
||||
sinon.assert.calledWith(res.sendStatus, 400);
|
||||
});
|
||||
|
||||
it('sends a 404 on failure', function() {
|
||||
storage.setField.throws(new Error());
|
||||
const req = request('x', { auth: 'z' });
|
||||
const res = response();
|
||||
passwordRoute(req, res);
|
||||
sinon.assert.calledWith(res.sendStatus, 404);
|
||||
});
|
||||
});
|
154
test/backend/s3-tests.js
Normal file
154
test/backend/s3-tests.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
const assert = require('assert');
|
||||
const sinon = require('sinon');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
function resolvedPromise(val) {
|
||||
return {
|
||||
promise: () => Promise.resolve(val)
|
||||
};
|
||||
}
|
||||
|
||||
function rejectedPromise(err) {
|
||||
return {
|
||||
promise: () => Promise.reject(err)
|
||||
};
|
||||
}
|
||||
|
||||
const s3Stub = {
|
||||
headObject: sinon.stub(),
|
||||
getObject: sinon.stub(),
|
||||
upload: sinon.stub(),
|
||||
deleteObject: sinon.stub()
|
||||
};
|
||||
|
||||
const awsStub = {
|
||||
S3: function() {
|
||||
return s3Stub;
|
||||
}
|
||||
};
|
||||
|
||||
const S3Storage = proxyquire('../../server/storage/s3', {
|
||||
'aws-sdk': awsStub
|
||||
});
|
||||
|
||||
describe('S3Storage', function() {
|
||||
it('uses config.s3_bucket', function() {
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
assert.equal(s.bucket, 'foo');
|
||||
});
|
||||
|
||||
describe('length', function() {
|
||||
it('returns the ContentLength', async function() {
|
||||
s3Stub.headObject = sinon
|
||||
.stub()
|
||||
.returns(resolvedPromise({ ContentLength: 123 }));
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
const len = await s.length('x');
|
||||
assert.equal(len, 123);
|
||||
sinon.assert.calledWithMatch(s3Stub.headObject, {
|
||||
Bucket: 'foo',
|
||||
Key: 'x'
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when id not found', async function() {
|
||||
const err = new Error();
|
||||
s3Stub.headObject = sinon.stub().returns(rejectedPromise(err));
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
try {
|
||||
await s.length('x');
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
assert.equal(e, err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStream', function() {
|
||||
it('returns a Stream object', function() {
|
||||
const stream = {};
|
||||
s3Stub.getObject = sinon
|
||||
.stub()
|
||||
.returns({ createReadStream: () => stream });
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
const result = s.getStream('x');
|
||||
assert.equal(result, stream);
|
||||
sinon.assert.calledWithMatch(s3Stub.getObject, {
|
||||
Bucket: 'foo',
|
||||
Key: 'x'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('set', function() {
|
||||
it('calls s3.upload', async function() {
|
||||
const file = { on: sinon.stub() };
|
||||
s3Stub.upload = sinon.stub().returns(resolvedPromise());
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
await s.set('x', file);
|
||||
sinon.assert.calledWithMatch(s3Stub.upload, {
|
||||
Bucket: 'foo',
|
||||
Key: 'x',
|
||||
Body: file
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts upload if limit is hit', async function() {
|
||||
const file = {
|
||||
on: (ev, fn) => fn()
|
||||
};
|
||||
const abort = sinon.stub();
|
||||
const err = new Error();
|
||||
s3Stub.upload = sinon.stub().returns({
|
||||
promise: () => Promise.reject(err),
|
||||
abort
|
||||
});
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
try {
|
||||
await s.set('x', file);
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
assert.equal(e.message, 'limit');
|
||||
sinon.assert.calledOnce(abort);
|
||||
}
|
||||
});
|
||||
|
||||
it('throws when s3.upload fails', async function() {
|
||||
const file = {
|
||||
on: sinon.stub()
|
||||
};
|
||||
const err = new Error();
|
||||
s3Stub.upload = sinon.stub().returns(rejectedPromise(err));
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
try {
|
||||
await s.set('x', file);
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
assert.equal(e, err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('del', function() {
|
||||
it('calls s3.deleteObject', async function() {
|
||||
s3Stub.deleteObject = sinon.stub().returns(resolvedPromise(true));
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
const result = await s.del('x');
|
||||
assert.equal(result, true);
|
||||
sinon.assert.calledWithMatch(s3Stub.deleteObject, {
|
||||
Bucket: 'foo',
|
||||
Key: 'x'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ping', function() {
|
||||
it('calls s3.headBucket', async function() {
|
||||
s3Stub.headBucket = sinon.stub().returns(resolvedPromise(true));
|
||||
const s = new S3Storage({ s3_bucket: 'foo' });
|
||||
const result = await s.ping();
|
||||
assert.equal(result, true);
|
||||
sinon.assert.calledWithMatch(s3Stub.headBucket, { Bucket: 'foo' });
|
||||
});
|
||||
});
|
||||
});
|
118
test/backend/storage-tests.js
Normal file
118
test/backend/storage-tests.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
const assert = require('assert');
|
||||
const proxyquire = require('proxyquire').noCallThru();
|
||||
|
||||
const stream = {};
|
||||
class MockStorage {
|
||||
length() {
|
||||
return Promise.resolve(12);
|
||||
}
|
||||
getStream() {
|
||||
return stream;
|
||||
}
|
||||
set() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
del() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
ping() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
const expire_seconds = 10;
|
||||
const storage = proxyquire('../../server/storage', {
|
||||
'../config': {
|
||||
expire_seconds,
|
||||
s3_bucket: 'foo',
|
||||
env: 'development',
|
||||
redis_host: 'localhost'
|
||||
},
|
||||
'../log': () => {},
|
||||
'./s3': MockStorage
|
||||
});
|
||||
|
||||
describe('Storage', function() {
|
||||
describe('ttl', function() {
|
||||
it('returns milliseconds remaining', async function() {
|
||||
await storage.set('x', null, { foo: 'bar' });
|
||||
const ms = await storage.ttl('x');
|
||||
await storage.del('x');
|
||||
assert.equal(ms, expire_seconds * 1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('length', function() {
|
||||
it('returns the file size', async function() {
|
||||
const len = await storage.length('x');
|
||||
assert.equal(len, 12);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', function() {
|
||||
it('returns a stream', function() {
|
||||
const s = storage.get('x');
|
||||
assert.equal(s, stream);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set', function() {
|
||||
it('sets expiration to config.expire_seconds', async function() {
|
||||
await storage.set('x', null, { foo: 'bar' });
|
||||
const s = await storage.redis.ttlAsync('x');
|
||||
await storage.del('x');
|
||||
assert.equal(Math.ceil(s), expire_seconds);
|
||||
});
|
||||
|
||||
it('sets metadata', async function() {
|
||||
const m = { foo: 'bar' };
|
||||
await storage.set('x', null, m);
|
||||
const meta = await storage.redis.hgetallAsync('x');
|
||||
await storage.del('x');
|
||||
assert.deepEqual(meta, m);
|
||||
});
|
||||
|
||||
//it('throws when storage fails');
|
||||
});
|
||||
|
||||
describe('setField', function() {
|
||||
it('works', async function() {
|
||||
storage.setField('x', 'y', 'z');
|
||||
const z = await storage.redis.hgetAsync('x', 'y');
|
||||
assert.equal(z, 'z');
|
||||
await storage.del('x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('del', function() {
|
||||
it('works', async function() {
|
||||
await storage.set('x', null, { foo: 'bar' });
|
||||
await storage.del('x');
|
||||
const meta = await storage.metadata('x');
|
||||
assert.equal(meta, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ping', function() {
|
||||
it('works', async function() {
|
||||
await storage.ping();
|
||||
});
|
||||
});
|
||||
|
||||
describe('metadata', function() {
|
||||
it('returns all metadata fields', async function() {
|
||||
const m = {
|
||||
pwd: true,
|
||||
dl: 1,
|
||||
dlimit: 1,
|
||||
auth: 'foo',
|
||||
metadata: 'bar',
|
||||
nonce: 'baz',
|
||||
owner: 'bmo'
|
||||
};
|
||||
await storage.set('x', null, m);
|
||||
const meta = await storage.metadata('x');
|
||||
assert.deepEqual(meta, m);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue