overleaf/services/filestore/test/unit/coffee/AWSSDKPersistorManagerTests.js

354 lines
12 KiB
JavaScript
Raw Normal View History

/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const sinon = require('sinon');
const chai = require('chai');
const should = chai.should();
const {
expect
} = chai;
const modulePath = "../../../app/js/AWSSDKPersistorManager.js";
const SandboxedModule = require('sandboxed-module');
describe("AWSSDKPersistorManager", function() {
beforeEach(function() {
this.settings = {
filestore: {
backend: "aws-sdk"
}
};
this.s3 = {
upload: sinon.stub(),
getObject: sinon.stub(),
copyObject: sinon.stub(),
deleteObject: sinon.stub(),
listObjects: sinon.stub(),
deleteObjects: sinon.stub(),
headObject: sinon.stub()
};
this.awssdk =
{S3: sinon.stub().returns(this.s3)};
this.requires = {
"aws-sdk": this.awssdk,
"settings-sharelatex": this.settings,
"logger-sharelatex": {
log() {},
err() {}
},
"fs": (this.fs =
{createReadStream: sinon.stub()}),
"./Errors": (this.Errors =
{NotFoundError: sinon.stub()})
};
this.key = "my/key";
this.bucketName = "my-bucket";
this.error = "my error";
return this.AWSSDKPersistorManager = SandboxedModule.require(modulePath, {requires: this.requires});
});
describe("sendFile", function() {
beforeEach(function() {
this.stream = {};
this.fsPath = "/usr/local/some/file";
return this.fs.createReadStream.returns(this.stream);
});
it("should put the file with s3.upload", function(done) {
this.s3.upload.callsArgWith(1);
return this.AWSSDKPersistorManager.sendFile(this.bucketName, this.key, this.fsPath, err => {
expect(err).to.not.be.ok;
expect(this.s3.upload.calledOnce, "called only once").to.be.true;
expect((this.s3.upload.calledWith({Bucket: this.bucketName, Key: this.key, Body: this.stream}))
, "called with correct arguments").to.be.true;
return done();
});
});
return it("should dispatch the error from s3.upload", function(done) {
this.s3.upload.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.sendFile(this.bucketName, this.key, this.fsPath, err => {
expect(err).to.equal(this.error);
return done();
});
});
});
describe("sendStream", function() {
beforeEach(function() {
return this.stream = {};});
it("should put the file with s3.upload", function(done) {
this.s3.upload.callsArgWith(1);
return this.AWSSDKPersistorManager.sendStream(this.bucketName, this.key, this.stream, err => {
expect(err).to.not.be.ok;
expect(this.s3.upload.calledOnce, "called only once").to.be.true;
expect((this.s3.upload.calledWith({Bucket: this.bucketName, Key: this.key, Body: this.stream})),
"called with correct arguments").to.be.true;
return done();
});
});
return it("should dispatch the error from s3.upload", function(done) {
this.s3.upload.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.sendStream(this.bucketName, this.key, this.stream, err => {
expect(err).to.equal(this.error);
return done();
});
});
});
describe("getFileStream", function() {
beforeEach(function() {
this.opts = {};
this.stream = {};
this.read_stream =
{on: (this.read_stream_on = sinon.stub())};
this.object =
{createReadStream: sinon.stub().returns(this.read_stream)};
return this.s3.getObject.returns(this.object);
});
it("should return a stream from s3.getObject", function(done) {
this.read_stream_on.withArgs('readable').callsArgWith(1);
return this.AWSSDKPersistorManager.getFileStream(this.bucketName, this.key, this.opts, (err, stream) => {
expect(this.read_stream_on.calledTwice);
expect(err).to.not.be.ok;
expect(stream, "returned the stream").to.equal(this.read_stream);
expect((this.s3.getObject.calledWith({Bucket: this.bucketName, Key: this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
describe("with start and end options", function() {
beforeEach(function() {
return this.opts = {
start: 0,
end: 8
};
});
return it("should pass headers to the s3.GetObject", function(done) {
this.read_stream_on.withArgs('readable').callsArgWith(1);
this.AWSSDKPersistorManager.getFileStream(this.bucketName, this.key, this.opts, (err, stream) => {
return expect((this.s3.getObject.calledWith({Bucket: this.bucketName, Key: this.key, Range: 'bytes=0-8'})),
"called with correct arguments").to.be.true;
});
return done();
});
});
return describe("error conditions", function() {
describe("when the file doesn't exist", function() {
beforeEach(function() {
this.error = new Error();
return this.error.code = 'NoSuchKey';
});
return it("should produce a NotFoundError", function(done) {
this.read_stream_on.withArgs('error').callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.getFileStream(this.bucketName, this.key, this.opts, (err, stream) => {
expect(stream).to.not.be.ok;
expect(err).to.be.ok;
expect(err instanceof this.Errors.NotFoundError, "error is a correct instance").to.equal(true);
return done();
});
});
});
return describe("when there is some other error", function() {
beforeEach(function() {
return this.error = new Error();
});
return it("should dispatch the error from s3 object stream", function(done) {
this.read_stream_on.withArgs('error').callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.getFileStream(this.bucketName, this.key, this.opts, (err, stream) => {
expect(stream).to.not.be.ok;
expect(err).to.be.ok;
expect(err).to.equal(this.error);
return done();
});
});
});
});
});
describe("copyFile", function() {
beforeEach(function() {
this.destKey = "some/key";
return this.stream = {};});
it("should copy the file with s3.copyObject", function(done) {
this.s3.copyObject.callsArgWith(1);
return this.AWSSDKPersistorManager.copyFile(this.bucketName, this.key, this.destKey, err => {
expect(err).to.not.be.ok;
expect(this.s3.copyObject.calledOnce, "called only once").to.be.true;
expect((this.s3.copyObject.calledWith({Bucket: this.bucketName, Key: this.destKey, CopySource: this.bucketName + '/' + this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
return it("should dispatch the error from s3.copyObject", function(done) {
this.s3.copyObject.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.copyFile(this.bucketName, this.key, this.destKey, err => {
expect(err).to.equal(this.error);
return done();
});
});
});
describe("deleteFile", function() {
it("should delete the file with s3.deleteObject", function(done) {
this.s3.deleteObject.callsArgWith(1);
return this.AWSSDKPersistorManager.deleteFile(this.bucketName, this.key, err => {
expect(err).to.not.be.ok;
expect(this.s3.deleteObject.calledOnce, "called only once").to.be.true;
expect((this.s3.deleteObject.calledWith({Bucket: this.bucketName, Key: this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
return it("should dispatch the error from s3.deleteObject", function(done) {
this.s3.deleteObject.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.deleteFile(this.bucketName, this.key, err => {
expect(err).to.equal(this.error);
return done();
});
});
});
describe("deleteDirectory", function() {
it("should list the directory content using s3.listObjects", function(done) {
this.s3.listObjects.callsArgWith(1, null, {Contents: []});
return this.AWSSDKPersistorManager.deleteDirectory(this.bucketName, this.key, err => {
expect(err).to.not.be.ok;
expect(this.s3.listObjects.calledOnce, "called only once").to.be.true;
expect((this.s3.listObjects.calledWith({Bucket: this.bucketName, Prefix: this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
it("should dispatch the error from s3.listObjects", function(done) {
this.s3.listObjects.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.deleteDirectory(this.bucketName, this.key, err => {
expect(err).to.equal(this.error);
return done();
});
});
return describe("with directory content", function() {
beforeEach(function() {
return this.fileList = [
{Key: 'foo'}
, { Key: 'bar'
, Key: 'baz'
}
];});
it("should forward the file keys to s3.deleteObjects", function(done) {
this.s3.listObjects.callsArgWith(1, null, {Contents: this.fileList});
this.s3.deleteObjects.callsArgWith(1);
return this.AWSSDKPersistorManager.deleteDirectory(this.bucketName, this.key, err => {
expect(err).to.not.be.ok;
expect(this.s3.deleteObjects.calledOnce, "called only once").to.be.true;
expect((this.s3.deleteObjects.calledWith({
Bucket: this.bucketName,
Delete: {
Quiet: true,
Objects: this.fileList
}})),
"called with correct arguments").to.be.true;
return done();
});
});
return it("should dispatch the error from s3.deleteObjects", function(done) {
this.s3.listObjects.callsArgWith(1, null, {Contents: this.fileList});
this.s3.deleteObjects.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.deleteDirectory(this.bucketName, this.key, err => {
expect(err).to.equal(this.error);
return done();
});
});
});
});
describe("checkIfFileExists", function() {
it("should check for the file with s3.headObject", function(done) {
this.s3.headObject.callsArgWith(1, null, {});
return this.AWSSDKPersistorManager.checkIfFileExists(this.bucketName, this.key, (err, exists) => {
expect(err).to.not.be.ok;
expect(this.s3.headObject.calledOnce, "called only once").to.be.true;
expect((this.s3.headObject.calledWith({Bucket: this.bucketName, Key: this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
it("should return false on an inexistant file", function(done) {
this.s3.headObject.callsArgWith(1, null, {});
return this.AWSSDKPersistorManager.checkIfFileExists(this.bucketName, this.key, (err, exists) => {
expect(exists).to.be.false;
return done();
});
});
it("should return true on an existing file", function(done) {
this.s3.headObject.callsArgWith(1, null, {ETag: "etag"});
return this.AWSSDKPersistorManager.checkIfFileExists(this.bucketName, this.key, (err, exists) => {
expect(exists).to.be.true;
return done();
});
});
return it("should dispatch the error from s3.headObject", function(done) {
this.s3.headObject.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.checkIfFileExists(this.bucketName, this.key, (err, exists) => {
expect(err).to.equal(this.error);
return done();
});
});
});
return describe("directorySize", function() {
it("should list the directory content using s3.listObjects", function(done) {
this.s3.listObjects.callsArgWith(1, null, {Contents: []});
return this.AWSSDKPersistorManager.directorySize(this.bucketName, this.key, err => {
expect(err).to.not.be.ok;
expect(this.s3.listObjects.calledOnce, "called only once").to.be.true;
expect((this.s3.listObjects.calledWith({Bucket: this.bucketName, Prefix: this.key})),
"called with correct arguments").to.be.true;
return done();
});
});
it("should dispatch the error from s3.listObjects", function(done) {
this.s3.listObjects.callsArgWith(1, this.error);
return this.AWSSDKPersistorManager.directorySize(this.bucketName, this.key, err => {
expect(err).to.equal(this.error);
return done();
});
});
return it("should sum directory files sizes", function(done) {
this.s3.listObjects.callsArgWith(1, null, {Contents: [ { Size: 1024 }, { Size: 2048 }]});
return this.AWSSDKPersistorManager.directorySize(this.bucketName, this.key, (err, size) => {
expect(size).to.equal(3072);
return done();
});
});
});
});