mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
decaffeinate: Convert ApplyUpdateTests.coffee and 18 other files to JS
This commit is contained in:
committed by
Jakob Ackermann
parent
30a9c6ed2c
commit
d318e4fd0e
@@ -1,222 +1,317 @@
|
||||
async = require "async"
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS201: Simplify complex destructure assignments
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const async = require("async");
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(settings.redis.documentupdater)
|
||||
const settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(settings.redis.documentupdater);
|
||||
|
||||
redisSettings = settings.redis
|
||||
const redisSettings = settings.redis;
|
||||
|
||||
describe "applyOtUpdate", ->
|
||||
before ->
|
||||
@update = {
|
||||
describe("applyOtUpdate", function() {
|
||||
before(function() {
|
||||
return this.update = {
|
||||
op: [{i: "foo", p: 42}]
|
||||
}
|
||||
describe "when authorized", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
};});
|
||||
describe("when authorized", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "applyOtUpdate", @doc_id, @update, cb
|
||||
], done
|
||||
|
||||
it "should push the doc into the pending updates list", (done) ->
|
||||
rclient.lrange "pending-updates-list", 0, -1, (error, [doc_id]) =>
|
||||
doc_id.should.equal "#{@project_id}:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
|
||||
it "should push the update into redis", (done) ->
|
||||
rclient.lrange redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), 0, -1, (error, [update]) =>
|
||||
update = JSON.parse(update)
|
||||
update.op.should.deep.equal @update.op
|
||||
update.meta.should.deep.equal {
|
||||
source: @client.publicId
|
||||
user_id: @user_id
|
||||
cb => {
|
||||
return this.client.emit("applyOtUpdate", this.doc_id, this.update, cb);
|
||||
}
|
||||
done()
|
||||
return null
|
||||
|
||||
after (done) ->
|
||||
async.series [
|
||||
(cb) => rclient.del "pending-updates-list", cb
|
||||
(cb) => rclient.del "DocsWithPendingUpdates", "#{@project_id}:#{@doc_id}", cb
|
||||
(cb) => rclient.del redisSettings.documentupdater.key_schema.pendingUpdates(@doc_id), cb
|
||||
], done
|
||||
], done);
|
||||
});
|
||||
|
||||
describe "when authorized with a huge edit update", ->
|
||||
before (done) ->
|
||||
@update = {
|
||||
it("should push the doc into the pending updates list", function(done) {
|
||||
rclient.lrange("pending-updates-list", 0, -1, (error, ...rest) => {
|
||||
const [doc_id] = Array.from(rest[0]);
|
||||
doc_id.should.equal(`${this.project_id}:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it("should push the update into redis", function(done) {
|
||||
rclient.lrange(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), 0, -1, (error, ...rest) => {
|
||||
let [update] = Array.from(rest[0]);
|
||||
update = JSON.parse(update);
|
||||
update.op.should.deep.equal(this.update.op);
|
||||
update.meta.should.deep.equal({
|
||||
source: this.client.publicId,
|
||||
user_id: this.user_id
|
||||
});
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
return after(function(done) {
|
||||
return async.series([
|
||||
cb => rclient.del("pending-updates-list", cb),
|
||||
cb => rclient.del("DocsWithPendingUpdates", `${this.project_id}:${this.doc_id}`, cb),
|
||||
cb => rclient.del(redisSettings.documentupdater.key_schema.pendingUpdates(this.doc_id), cb)
|
||||
], done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when authorized with a huge edit update", function() {
|
||||
before(function(done) {
|
||||
this.update = {
|
||||
op: {
|
||||
p: 12,
|
||||
t: "update is too large".repeat(1024 * 400) # >7MB
|
||||
t: "update is too large".repeat(1024 * 400) // >7MB
|
||||
}
|
||||
}
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
};
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
@client.on "otUpdateError", (@otUpdateError) =>
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "applyOtUpdate", @doc_id, @update, (@error) =>
|
||||
cb()
|
||||
], done
|
||||
|
||||
it "should not return an error", ->
|
||||
expect(@error).to.not.exist
|
||||
|
||||
it "should send an otUpdateError to the client", (done) ->
|
||||
setTimeout () =>
|
||||
expect(@otUpdateError).to.exist
|
||||
done()
|
||||
, 300
|
||||
|
||||
it "should disconnect the client", (done) ->
|
||||
setTimeout () =>
|
||||
@client.socket.connected.should.equal false
|
||||
done()
|
||||
, 300
|
||||
|
||||
it "should not put the update in redis", (done) ->
|
||||
rclient.llen redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), (error, len) =>
|
||||
len.should.equal 0
|
||||
done()
|
||||
return null
|
||||
|
||||
describe "when authorized to read-only with an edit update", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "readOnly"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
this.client.on("connectionAccepted", cb);
|
||||
return this.client.on("otUpdateError", otUpdateError => {
|
||||
this.otUpdateError = otUpdateError;
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "applyOtUpdate", @doc_id, @update, (@error) =>
|
||||
cb()
|
||||
], done
|
||||
|
||||
it "should return an error", ->
|
||||
expect(@error).to.exist
|
||||
|
||||
it "should disconnect the client", (done) ->
|
||||
setTimeout () =>
|
||||
@client.socket.connected.should.equal false
|
||||
done()
|
||||
, 300
|
||||
|
||||
it "should not put the update in redis", (done) ->
|
||||
rclient.llen redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), (error, len) =>
|
||||
len.should.equal 0
|
||||
done()
|
||||
return null
|
||||
|
||||
describe "when authorized to read-only with a comment update", ->
|
||||
before (done) ->
|
||||
@comment_update = {
|
||||
op: [{c: "foo", p: 42}]
|
||||
}
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "readOnly"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, cb
|
||||
|
||||
(cb) =>
|
||||
@client.emit "applyOtUpdate", @doc_id, @comment_update, cb
|
||||
], done
|
||||
|
||||
it "should push the doc into the pending updates list", (done) ->
|
||||
rclient.lrange "pending-updates-list", 0, -1, (error, [doc_id]) =>
|
||||
doc_id.should.equal "#{@project_id}:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
it "should push the update into redis", (done) ->
|
||||
rclient.lrange redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), 0, -1, (error, [update]) =>
|
||||
update = JSON.parse(update)
|
||||
update.op.should.deep.equal @comment_update.op
|
||||
update.meta.should.deep.equal {
|
||||
source: @client.publicId
|
||||
user_id: @user_id
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("applyOtUpdate", this.doc_id, this.update, error => {
|
||||
this.error = error;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
done()
|
||||
return null
|
||||
], done);
|
||||
});
|
||||
|
||||
after (done) ->
|
||||
async.series [
|
||||
(cb) => rclient.del "pending-updates-list", cb
|
||||
(cb) => rclient.del "DocsWithPendingUpdates", "#{@project_id}:#{@doc_id}", cb
|
||||
(cb) => rclient.del redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), cb
|
||||
], done
|
||||
it("should not return an error", function() {
|
||||
return expect(this.error).to.not.exist;
|
||||
});
|
||||
|
||||
it("should send an otUpdateError to the client", function(done) {
|
||||
return setTimeout(() => {
|
||||
expect(this.otUpdateError).to.exist;
|
||||
return done();
|
||||
}
|
||||
, 300);
|
||||
});
|
||||
|
||||
it("should disconnect the client", function(done) {
|
||||
return setTimeout(() => {
|
||||
this.client.socket.connected.should.equal(false);
|
||||
return done();
|
||||
}
|
||||
, 300);
|
||||
});
|
||||
|
||||
return it("should not put the update in redis", function(done) {
|
||||
rclient.llen(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), (error, len) => {
|
||||
len.should.equal(0);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe("when authorized to read-only with an edit update", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readOnly"
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("applyOtUpdate", this.doc_id, this.update, error => {
|
||||
this.error = error;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it("should return an error", function() {
|
||||
return expect(this.error).to.exist;
|
||||
});
|
||||
|
||||
it("should disconnect the client", function(done) {
|
||||
return setTimeout(() => {
|
||||
this.client.socket.connected.should.equal(false);
|
||||
return done();
|
||||
}
|
||||
, 300);
|
||||
});
|
||||
|
||||
return it("should not put the update in redis", function(done) {
|
||||
rclient.llen(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), (error, len) => {
|
||||
len.should.equal(0);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
return describe("when authorized to read-only with a comment update", function() {
|
||||
before(function(done) {
|
||||
this.comment_update = {
|
||||
op: [{c: "foo", p: 42}]
|
||||
};
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readOnly"
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
cb => {
|
||||
return this.client.emit("applyOtUpdate", this.doc_id, this.comment_update, cb);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it("should push the doc into the pending updates list", function(done) {
|
||||
rclient.lrange("pending-updates-list", 0, -1, (error, ...rest) => {
|
||||
const [doc_id] = Array.from(rest[0]);
|
||||
doc_id.should.equal(`${this.project_id}:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it("should push the update into redis", function(done) {
|
||||
rclient.lrange(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), 0, -1, (error, ...rest) => {
|
||||
let [update] = Array.from(rest[0]);
|
||||
update = JSON.parse(update);
|
||||
update.op.should.deep.equal(this.comment_update.op);
|
||||
update.meta.should.deep.equal({
|
||||
source: this.client.publicId,
|
||||
user_id: this.user_id
|
||||
});
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
return after(function(done) {
|
||||
return async.series([
|
||||
cb => rclient.del("pending-updates-list", cb),
|
||||
cb => rclient.del("DocsWithPendingUpdates", `${this.project_id}:${this.doc_id}`, cb),
|
||||
cb => rclient.del(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), cb)
|
||||
], done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,146 +1,191 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockWebServer = require "./helpers/MockWebServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockWebServer = require("./helpers/MockWebServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
describe "clientTracking", ->
|
||||
describe "when a client updates its cursor location", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("clientTracking", function() {
|
||||
describe("when a client updates its cursor location", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: { name: "Test Project" }
|
||||
}, (error, {@user_id, @project_id}) => cb()
|
||||
}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB = RealTimeClient.connect()
|
||||
@clientB.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientB = RealTimeClient.connect();
|
||||
return this.clientB.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.clientB.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@updates = []
|
||||
@clientB.on "clientTracking.clientUpdated", (data) =>
|
||||
@updates.push data
|
||||
cb => {
|
||||
this.updates = [];
|
||||
this.clientB.on("clientTracking.clientUpdated", data => {
|
||||
return this.updates.push(data);
|
||||
});
|
||||
|
||||
@clientA.emit "clientTracking.updatePosition", {
|
||||
row: @row = 42
|
||||
column: @column = 36
|
||||
doc_id: @doc_id
|
||||
}, (error) ->
|
||||
throw error if error?
|
||||
setTimeout cb, 300 # Give the message a chance to reach client B.
|
||||
], done
|
||||
return this.clientA.emit("clientTracking.updatePosition", {
|
||||
row: (this.row = 42),
|
||||
column: (this.column = 36),
|
||||
doc_id: this.doc_id
|
||||
}, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(cb, 300);
|
||||
});
|
||||
} // Give the message a chance to reach client B.
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should tell other clients about the update", ->
|
||||
@updates.should.deep.equal [
|
||||
it("should tell other clients about the update", function() {
|
||||
return this.updates.should.deep.equal([
|
||||
{
|
||||
row: @row
|
||||
column: @column
|
||||
doc_id: @doc_id
|
||||
id: @clientA.publicId
|
||||
user_id: @user_id
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
doc_id: this.doc_id,
|
||||
id: this.clientA.publicId,
|
||||
user_id: this.user_id,
|
||||
name: "Joe Bloggs"
|
||||
}
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
it "should record the update in getConnectedUsers", (done) ->
|
||||
@clientB.emit "clientTracking.getConnectedUsers", (error, users) =>
|
||||
for user in users
|
||||
if user.client_id == @clientA.publicId
|
||||
return it("should record the update in getConnectedUsers", function(done) {
|
||||
return this.clientB.emit("clientTracking.getConnectedUsers", (error, users) => {
|
||||
for (let user of Array.from(users)) {
|
||||
if (user.client_id === this.clientA.publicId) {
|
||||
expect(user.cursorData).to.deep.equal({
|
||||
row: @row
|
||||
column: @column
|
||||
doc_id: @doc_id
|
||||
})
|
||||
return done()
|
||||
throw new Error("user was never found")
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
doc_id: this.doc_id
|
||||
});
|
||||
return done();
|
||||
}
|
||||
}
|
||||
throw new Error("user was never found");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when an anonymous client updates its cursor location", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
project: { name: "Test Project" }
|
||||
return describe("when an anonymous client updates its cursor location", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: { name: "Test Project" },
|
||||
publicAccess: "readAndWrite"
|
||||
}, (error, {@user_id, @project_id}) => cb()
|
||||
}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
RealTimeClient.setSession({}, cb)
|
||||
cb => {
|
||||
return RealTimeClient.setSession({}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@anonymous = RealTimeClient.connect()
|
||||
@anonymous.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.anonymous = RealTimeClient.connect();
|
||||
return this.anonymous.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@anonymous.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.anonymous.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@anonymous.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.anonymous.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@updates = []
|
||||
@clientA.on "clientTracking.clientUpdated", (data) =>
|
||||
@updates.push data
|
||||
cb => {
|
||||
this.updates = [];
|
||||
this.clientA.on("clientTracking.clientUpdated", data => {
|
||||
return this.updates.push(data);
|
||||
});
|
||||
|
||||
@anonymous.emit "clientTracking.updatePosition", {
|
||||
row: @row = 42
|
||||
column: @column = 36
|
||||
doc_id: @doc_id
|
||||
}, (error) ->
|
||||
throw error if error?
|
||||
setTimeout cb, 300 # Give the message a chance to reach client B.
|
||||
], done
|
||||
return this.anonymous.emit("clientTracking.updatePosition", {
|
||||
row: (this.row = 42),
|
||||
column: (this.column = 36),
|
||||
doc_id: this.doc_id
|
||||
}, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(cb, 300);
|
||||
});
|
||||
} // Give the message a chance to reach client B.
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should tell other clients about the update", ->
|
||||
@updates.should.deep.equal [
|
||||
return it("should tell other clients about the update", function() {
|
||||
return this.updates.should.deep.equal([
|
||||
{
|
||||
row: @row
|
||||
column: @column
|
||||
doc_id: @doc_id
|
||||
id: @anonymous.publicId
|
||||
user_id: "anonymous-user"
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
doc_id: this.doc_id,
|
||||
id: this.anonymous.publicId,
|
||||
user_id: "anonymous-user",
|
||||
name: ""
|
||||
}
|
||||
]
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,81 +1,100 @@
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
expect = require("chai").expect
|
||||
const {
|
||||
expect
|
||||
} = require("chai");
|
||||
|
||||
async = require "async"
|
||||
request = require "request"
|
||||
const async = require("async");
|
||||
const request = require("request");
|
||||
|
||||
Settings = require "settings-sharelatex"
|
||||
const Settings = require("settings-sharelatex");
|
||||
|
||||
drain = (rate, callback) ->
|
||||
request.post {
|
||||
url: "http://localhost:3026/drain?rate=#{rate}"
|
||||
const drain = function(rate, callback) {
|
||||
request.post({
|
||||
url: `http://localhost:3026/drain?rate=${rate}`,
|
||||
auth: {
|
||||
user: Settings.internal.realTime.user,
|
||||
pass: Settings.internal.realTime.pass
|
||||
}
|
||||
}, (error, response, data) ->
|
||||
callback error, data
|
||||
return null
|
||||
}, (error, response, data) => callback(error, data));
|
||||
return null;
|
||||
};
|
||||
|
||||
describe "DrainManagerTests", ->
|
||||
before (done) ->
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("DrainManagerTests", function() {
|
||||
before(function(done) {
|
||||
FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => done()
|
||||
return null
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return done(); });
|
||||
return null;
|
||||
});
|
||||
|
||||
before (done) ->
|
||||
# cleanup to speedup reconnecting
|
||||
@timeout(10000)
|
||||
RealTimeClient.disconnectAllClients done
|
||||
before(function(done) {
|
||||
// cleanup to speedup reconnecting
|
||||
this.timeout(10000);
|
||||
return RealTimeClient.disconnectAllClients(done);
|
||||
});
|
||||
|
||||
# trigger and check cleanup
|
||||
it "should have disconnected all previous clients", (done) ->
|
||||
RealTimeClient.getConnectedClients (error, data) ->
|
||||
return done(error) if error
|
||||
expect(data.length).to.equal(0)
|
||||
done()
|
||||
// trigger and check cleanup
|
||||
it("should have disconnected all previous clients", done => RealTimeClient.getConnectedClients(function(error, data) {
|
||||
if (error) { return done(error); }
|
||||
expect(data.length).to.equal(0);
|
||||
return done();
|
||||
}));
|
||||
|
||||
describe "with two clients in the project", ->
|
||||
beforeEach (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
return describe("with two clients in the project", function() {
|
||||
beforeEach(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB = RealTimeClient.connect()
|
||||
@clientB.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientB = RealTimeClient.connect();
|
||||
return this.clientB.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB.emit "joinProject", project_id: @project_id, cb
|
||||
], done
|
||||
cb => {
|
||||
return this.clientB.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
describe "starting to drain", () ->
|
||||
beforeEach (done) ->
|
||||
async.parallel [
|
||||
(cb) =>
|
||||
@clientA.on "reconnectGracefully", cb
|
||||
(cb) =>
|
||||
@clientB.on "reconnectGracefully", cb
|
||||
return describe("starting to drain", function() {
|
||||
beforeEach(function(done) {
|
||||
return async.parallel([
|
||||
cb => {
|
||||
return this.clientA.on("reconnectGracefully", cb);
|
||||
},
|
||||
cb => {
|
||||
return this.clientB.on("reconnectGracefully", cb);
|
||||
},
|
||||
|
||||
(cb) -> drain(2, cb)
|
||||
], done
|
||||
cb => drain(2, cb)
|
||||
], done);
|
||||
});
|
||||
|
||||
afterEach (done) ->
|
||||
drain(0, done) # reset drain
|
||||
afterEach(done => drain(0, done)); // reset drain
|
||||
|
||||
it "should not timeout", ->
|
||||
expect(true).to.equal(true)
|
||||
it("should not timeout", () => expect(true).to.equal(true));
|
||||
|
||||
it "should not have disconnected", ->
|
||||
expect(@clientA.socket.connected).to.equal true
|
||||
expect(@clientB.socket.connected).to.equal true
|
||||
return it("should not have disconnected", function() {
|
||||
expect(this.clientA.socket.connected).to.equal(true);
|
||||
return expect(this.clientB.socket.connected).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,160 +1,209 @@
|
||||
async = require "async"
|
||||
{expect} = require("chai")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const async = require("async");
|
||||
const {expect} = require("chai");
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
|
||||
MockWebServer = require "./helpers/MockWebServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
|
||||
const MockWebServer = require("./helpers/MockWebServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(settings.redis.pubsub)
|
||||
rclientRT = redis.createClient(settings.redis.realtime)
|
||||
KeysRT = settings.redis.realtime.key_schema
|
||||
const settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(settings.redis.pubsub);
|
||||
const rclientRT = redis.createClient(settings.redis.realtime);
|
||||
const KeysRT = settings.redis.realtime.key_schema;
|
||||
|
||||
describe "EarlyDisconnect", ->
|
||||
before (done) ->
|
||||
MockDocUpdaterServer.run done
|
||||
describe("EarlyDisconnect", function() {
|
||||
before(done => MockDocUpdaterServer.run(done));
|
||||
|
||||
describe "when the client disconnects before joinProject completes", ->
|
||||
before () ->
|
||||
# slow down web-api requests to force the race condition
|
||||
@actualWebAPIjoinProject = joinProject = MockWebServer.joinProject
|
||||
MockWebServer.joinProject = (project_id, user_id, cb) ->
|
||||
setTimeout () ->
|
||||
joinProject(project_id, user_id, cb)
|
||||
, 300
|
||||
describe("when the client disconnects before joinProject completes", function() {
|
||||
before(function() {
|
||||
// slow down web-api requests to force the race condition
|
||||
let joinProject;
|
||||
this.actualWebAPIjoinProject = (joinProject = MockWebServer.joinProject);
|
||||
return MockWebServer.joinProject = (project_id, user_id, cb) => setTimeout(() => joinProject(project_id, user_id, cb)
|
||||
, 300);
|
||||
});
|
||||
|
||||
after () ->
|
||||
MockWebServer.joinProject = @actualWebAPIjoinProject
|
||||
after(function() {
|
||||
return MockWebServer.joinProject = this.actualWebAPIjoinProject;
|
||||
});
|
||||
|
||||
beforeEach (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
beforeEach(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (() ->)
|
||||
# disconnect before joinProject completes
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
cb => {
|
||||
this.clientA.emit("joinProject", {project_id: this.project_id}, (function() {}));
|
||||
// disconnect before joinProject completes
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for joinDoc and subscribe
|
||||
setTimeout cb, 500
|
||||
], done
|
||||
cb => {
|
||||
// wait for joinDoc and subscribe
|
||||
return setTimeout(cb, 500);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
# we can force the race condition, there is no need to repeat too often
|
||||
for attempt in Array.from(length: 5).map((_, i) -> i+1)
|
||||
it "should not subscribe to the pub/sub channel anymore (race #{attempt})", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
expect(resp).to.not.include "editor-events:#{@project_id}"
|
||||
done()
|
||||
return null
|
||||
// we can force the race condition, there is no need to repeat too often
|
||||
return Array.from(Array.from({length: 5}).map((_, i) => i+1)).map((attempt) =>
|
||||
it(`should not subscribe to the pub/sub channel anymore (race ${attempt})`, function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
expect(resp).to.not.include(`editor-events:${this.project_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
}));
|
||||
});
|
||||
|
||||
describe "when the client disconnects before joinDoc completes", ->
|
||||
beforeEach (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("when the client disconnects before joinDoc completes", function() {
|
||||
beforeEach(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, (() ->)
|
||||
# disconnect before joinDoc completes
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
cb => {
|
||||
this.clientA.emit("joinDoc", this.doc_id, (function() {}));
|
||||
// disconnect before joinDoc completes
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for subscribe and unsubscribe
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for subscribe and unsubscribe
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
# we can not force the race condition, so we have to try many times
|
||||
for attempt in Array.from(length: 20).map((_, i) -> i+1)
|
||||
it "should not subscribe to the pub/sub channels anymore (race #{attempt})", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
expect(resp).to.not.include "editor-events:#{@project_id}"
|
||||
// we can not force the race condition, so we have to try many times
|
||||
return Array.from(Array.from({length: 20}).map((_, i) => i+1)).map((attempt) =>
|
||||
it(`should not subscribe to the pub/sub channels anymore (race ${attempt})`, function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
expect(resp).to.not.include(`editor-events:${this.project_id}`);
|
||||
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
expect(resp).to.not.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
expect(resp).to.not.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
}));
|
||||
});
|
||||
|
||||
describe "when the client disconnects before clientTracking.updatePosition starts", ->
|
||||
beforeEach (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
return describe("when the client disconnects before clientTracking.updatePosition starts", function() {
|
||||
beforeEach(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "clientTracking.updatePosition", {
|
||||
row: 42
|
||||
column: 36
|
||||
doc_id: @doc_id
|
||||
}, (() ->)
|
||||
# disconnect before updateClientPosition completes
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
cb => {
|
||||
this.clientA.emit("clientTracking.updatePosition", {
|
||||
row: 42,
|
||||
column: 36,
|
||||
doc_id: this.doc_id
|
||||
}, (function() {}));
|
||||
// disconnect before updateClientPosition completes
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for updateClientPosition
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for updateClientPosition
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
# we can not force the race condition, so we have to try many times
|
||||
for attempt in Array.from(length: 20).map((_, i) -> i+1)
|
||||
it "should not show the client as connected (race #{attempt})", (done) ->
|
||||
rclientRT.smembers KeysRT.clientsInProject({project_id: @project_id}), (err, results) ->
|
||||
return done(err) if err
|
||||
expect(results).to.deep.equal([])
|
||||
done()
|
||||
return null
|
||||
// we can not force the race condition, so we have to try many times
|
||||
return Array.from(Array.from({length: 20}).map((_, i) => i+1)).map((attempt) =>
|
||||
it(`should not show the client as connected (race ${attempt})`, function(done) {
|
||||
rclientRT.smembers(KeysRT.clientsInProject({project_id: this.project_id}), function(err, results) {
|
||||
if (err) { return done(err); }
|
||||
expect(results).to.deep.equal([]);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,68 +1,91 @@
|
||||
async = require('async')
|
||||
expect = require('chai').expect
|
||||
request = require('request').defaults({
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const async = require('async');
|
||||
const {
|
||||
expect
|
||||
} = require('chai');
|
||||
const request = require('request').defaults({
|
||||
baseUrl: 'http://localhost:3026'
|
||||
})
|
||||
});
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
describe 'HttpControllerTests', ->
|
||||
describe 'without a user', ->
|
||||
it 'should return 404 for the client view', (done) ->
|
||||
client_id = 'not-existing'
|
||||
request.get {
|
||||
url: "/clients/#{client_id}"
|
||||
json: true
|
||||
}, (error, response, data) ->
|
||||
return done(error) if error
|
||||
expect(response.statusCode).to.equal(404)
|
||||
done()
|
||||
describe('HttpControllerTests', function() {
|
||||
describe('without a user', () => it('should return 404 for the client view', function(done) {
|
||||
const client_id = 'not-existing';
|
||||
return request.get({
|
||||
url: `/clients/${client_id}`,
|
||||
json: true
|
||||
}, function(error, response, data) {
|
||||
if (error) { return done(error); }
|
||||
expect(response.statusCode).to.equal(404);
|
||||
return done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe 'with a user and after joining a project', ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
return describe('with a user and after joining a project', function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner"
|
||||
}, (error, {@project_id, @user_id}) =>
|
||||
cb(error)
|
||||
}, (error, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {}, (error, {@doc_id}) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {}, (error, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", {@project_id}, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, cb
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, cb);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it 'should send a client view', (done) ->
|
||||
request.get {
|
||||
url: "/clients/#{@client.socket.sessionid}"
|
||||
return it('should send a client view', function(done) {
|
||||
return request.get({
|
||||
url: `/clients/${this.client.socket.sessionid}`,
|
||||
json: true
|
||||
}, (error, response, data) =>
|
||||
return done(error) if error
|
||||
expect(response.statusCode).to.equal(200)
|
||||
expect(data.connected_time).to.exist
|
||||
delete data.connected_time
|
||||
# .email is not set in the session
|
||||
delete data.email
|
||||
}, (error, response, data) => {
|
||||
if (error) { return done(error); }
|
||||
expect(response.statusCode).to.equal(200);
|
||||
expect(data.connected_time).to.exist;
|
||||
delete data.connected_time;
|
||||
// .email is not set in the session
|
||||
delete data.email;
|
||||
expect(data).to.deep.equal({
|
||||
client_id: @client.socket.sessionid,
|
||||
client_id: this.client.socket.sessionid,
|
||||
first_name: 'Joe',
|
||||
last_name: 'Bloggs',
|
||||
project_id: @project_id,
|
||||
user_id: @user_id,
|
||||
project_id: this.project_id,
|
||||
user_id: this.user_id,
|
||||
rooms: [
|
||||
@project_id,
|
||||
@doc_id,
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
]
|
||||
})
|
||||
done()
|
||||
});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,246 +1,351 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
describe "joinDoc", ->
|
||||
before ->
|
||||
@lines = ["test", "doc", "lines"]
|
||||
@version = 42
|
||||
@ops = ["mock", "doc", "ops"]
|
||||
@ranges = {"mock": "ranges"}
|
||||
describe("joinDoc", function() {
|
||||
before(function() {
|
||||
this.lines = ["test", "doc", "lines"];
|
||||
this.version = 42;
|
||||
this.ops = ["mock", "doc", "ops"];
|
||||
return this.ranges = {"mock": "ranges"};});
|
||||
|
||||
describe "when authorised readAndWrite", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
describe("when authorised readAndWrite", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, -1)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, -1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when authorised readOnly", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
describe("when authorised readOnly", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readOnly"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, -1)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, -1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when authorised as owner", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
describe("when authorised as owner", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, -1)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, -1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
# It is impossible to write an acceptance test to test joining an unauthorized
|
||||
# project, since joinProject already catches that. If you can join a project,
|
||||
# then you can join a doc in that project.
|
||||
// It is impossible to write an acceptance test to test joining an unauthorized
|
||||
// project, since joinProject already catches that. If you can join a project,
|
||||
// then you can join a doc in that project.
|
||||
|
||||
describe "with a fromVersion", ->
|
||||
before (done) ->
|
||||
@fromVersion = 36
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
describe("with a fromVersion", function() {
|
||||
before(function(done) {
|
||||
this.fromVersion = 36;
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, @fromVersion, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, this.fromVersion, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater with the fromVersion", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, @fromVersion)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater with the fromVersion", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.fromVersion)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "with options", ->
|
||||
before (done) ->
|
||||
@options = { encodeRanges: true }
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
describe("with options", function() {
|
||||
before(function(done) {
|
||||
this.options = { encodeRanges: true };
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, @options, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, this.options, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater with the default fromVersion", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, -1)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater with the default fromVersion", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, -1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "with fromVersion and options", ->
|
||||
before (done) ->
|
||||
@fromVersion = 36
|
||||
@options = { encodeRanges: true }
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
return describe("with fromVersion and options", function() {
|
||||
before(function(done) {
|
||||
this.fromVersion = 36;
|
||||
this.options = { encodeRanges: true };
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, @fromVersion, @options, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, this.fromVersion, this.options, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the doc from the doc updater with the fromVersion", ->
|
||||
MockDocUpdaterServer.getDocument
|
||||
.calledWith(@project_id, @doc_id, @fromVersion)
|
||||
.should.equal true
|
||||
it("should get the doc from the doc updater with the fromVersion", function() {
|
||||
return MockDocUpdaterServer.getDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.fromVersion)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the doc lines, version, ranges and ops", ->
|
||||
@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
|
||||
it("should return the doc lines, version, ranges and ops", function() {
|
||||
return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
|
||||
});
|
||||
|
||||
it "should have joined the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal true
|
||||
done()
|
||||
return it("should have joined the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,108 +1,162 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockWebServer = require "./helpers/MockWebServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockWebServer = require("./helpers/MockWebServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
describe "joinProject", ->
|
||||
describe "when authorized", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("joinProject", function() {
|
||||
describe("when authorized", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should get the project from web", ->
|
||||
MockWebServer.joinProject
|
||||
.calledWith(@project_id, @user_id)
|
||||
.should.equal true
|
||||
it("should get the project from web", function() {
|
||||
return MockWebServer.joinProject
|
||||
.calledWith(this.project_id, this.user_id)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the project", ->
|
||||
@project.should.deep.equal {
|
||||
it("should return the project", function() {
|
||||
return this.project.should.deep.equal({
|
||||
name: "Test Project"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it "should return the privilege level", ->
|
||||
@privilegeLevel.should.equal "owner"
|
||||
it("should return the privilege level", function() {
|
||||
return this.privilegeLevel.should.equal("owner");
|
||||
});
|
||||
|
||||
it "should return the protocolVersion", ->
|
||||
@protocolVersion.should.equal 2
|
||||
it("should return the protocolVersion", function() {
|
||||
return this.protocolVersion.should.equal(2);
|
||||
});
|
||||
|
||||
it "should have joined the project room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@project_id in client.rooms).to.equal true
|
||||
done()
|
||||
it("should have joined the project room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.project_id)).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should have marked the user as connected", (done) ->
|
||||
@client.emit "clientTracking.getConnectedUsers", (error, users) =>
|
||||
connected = false
|
||||
for user in users
|
||||
if user.client_id == @client.publicId and user.user_id == @user_id
|
||||
connected = true
|
||||
break
|
||||
expect(connected).to.equal true
|
||||
done()
|
||||
return it("should have marked the user as connected", function(done) {
|
||||
return this.client.emit("clientTracking.getConnectedUsers", (error, users) => {
|
||||
let connected = false;
|
||||
for (let user of Array.from(users)) {
|
||||
if ((user.client_id === this.client.publicId) && (user.user_id === this.user_id)) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(connected).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when not authorized", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: null
|
||||
describe("when not authorized", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: null,
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, (@error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb()
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.error = error;
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should return an error", ->
|
||||
@error.message.should.equal "not authorized"
|
||||
it("should return an error", function() {
|
||||
return this.error.message.should.equal("not authorized");
|
||||
});
|
||||
|
||||
it "should not have joined the project room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@project_id in client.rooms).to.equal false
|
||||
done()
|
||||
return it("should not have joined the project room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.project_id)).to.equal(false);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when over rate limit", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
return describe("when over rate limit", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: 'rate-limited', (@error) =>
|
||||
cb()
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: 'rate-limited'}, error => {
|
||||
this.error = error;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should return a TooManyRequests error code", ->
|
||||
@error.message.should.equal "rate-limit hit when joining project"
|
||||
@error.code.should.equal "TooManyRequests"
|
||||
return it("should return a TooManyRequests error code", function() {
|
||||
this.error.message.should.equal("rate-limit hit when joining project");
|
||||
return this.error.code.should.equal("TooManyRequests");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,86 +1,121 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
sinon = require("sinon")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
const sinon = require("sinon");
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
logger = require("logger-sharelatex")
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
const logger = require("logger-sharelatex");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
describe "leaveDoc", ->
|
||||
before ->
|
||||
@lines = ["test", "doc", "lines"]
|
||||
@version = 42
|
||||
@ops = ["mock", "doc", "ops"]
|
||||
sinon.spy(logger, "error")
|
||||
sinon.spy(logger, "warn")
|
||||
sinon.spy(logger, "log")
|
||||
@other_doc_id = FixturesManager.getRandomId()
|
||||
describe("leaveDoc", function() {
|
||||
before(function() {
|
||||
this.lines = ["test", "doc", "lines"];
|
||||
this.version = 42;
|
||||
this.ops = ["mock", "doc", "ops"];
|
||||
sinon.spy(logger, "error");
|
||||
sinon.spy(logger, "warn");
|
||||
sinon.spy(logger, "log");
|
||||
return this.other_doc_id = FixturesManager.getRandomId();
|
||||
});
|
||||
|
||||
after ->
|
||||
logger.error.restore() # remove the spy
|
||||
logger.warn.restore()
|
||||
logger.log.restore()
|
||||
after(function() {
|
||||
logger.error.restore(); // remove the spy
|
||||
logger.warn.restore();
|
||||
return logger.log.restore();
|
||||
});
|
||||
|
||||
describe "when joined to a doc", ->
|
||||
beforeEach (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
return describe("when joined to a doc", function() {
|
||||
beforeEach(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "readAndWrite"
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id, cb
|
||||
cb => {
|
||||
return this.client.emit("joinProject", {project_id: this.project_id}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
describe "then leaving the doc", ->
|
||||
beforeEach (done) ->
|
||||
@client.emit "leaveDoc", @doc_id, (error) ->
|
||||
throw error if error?
|
||||
done()
|
||||
describe("then leaving the doc", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.client.emit("leaveDoc", this.doc_id, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should have left the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal false
|
||||
done()
|
||||
return it("should have left the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(false);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when sending a leaveDoc request before the previous joinDoc request has completed", ->
|
||||
beforeEach (done) ->
|
||||
@client.emit "leaveDoc", @doc_id, () ->
|
||||
@client.emit "joinDoc", @doc_id, () ->
|
||||
@client.emit "leaveDoc", @doc_id, (error) ->
|
||||
throw error if error?
|
||||
done()
|
||||
describe("when sending a leaveDoc request before the previous joinDoc request has completed", function() {
|
||||
beforeEach(function(done) {
|
||||
this.client.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.client.emit("joinDoc", this.doc_id, function() {});
|
||||
return this.client.emit("leaveDoc", this.doc_id, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should not trigger an error", ->
|
||||
sinon.assert.neverCalledWith(logger.error, sinon.match.any, "not subscribed - shouldn't happen")
|
||||
it("should not trigger an error", () => sinon.assert.neverCalledWith(logger.error, sinon.match.any, "not subscribed - shouldn't happen"));
|
||||
|
||||
it "should have left the doc room", (done) ->
|
||||
RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
|
||||
expect(@doc_id in client.rooms).to.equal false
|
||||
done()
|
||||
return it("should have left the doc room", function(done) {
|
||||
return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
|
||||
expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(false);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when sending a leaveDoc for a room the client has not joined ", ->
|
||||
beforeEach (done) ->
|
||||
@client.emit "leaveDoc", @other_doc_id, (error) ->
|
||||
throw error if error?
|
||||
done()
|
||||
return describe("when sending a leaveDoc for a room the client has not joined ", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.client.emit("leaveDoc", this.other_doc_id, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should trigger a low level message only", ->
|
||||
sinon.assert.calledWith(logger.log, sinon.match.any, "ignoring request from client to leave room it is not in")
|
||||
return it("should trigger a low level message only", () => sinon.assert.calledWith(logger.log, sinon.match.any, "ignoring request from client to leave room it is not in"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,147 +1,206 @@
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(settings.redis.pubsub)
|
||||
const settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(settings.redis.pubsub);
|
||||
|
||||
describe "leaveProject", ->
|
||||
before (done) ->
|
||||
MockDocUpdaterServer.run done
|
||||
describe("leaveProject", function() {
|
||||
before(done => MockDocUpdaterServer.run(done));
|
||||
|
||||
describe "with other clients in the project", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("with other clients in the project", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB = RealTimeClient.connect()
|
||||
@clientB.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientB = RealTimeClient.connect();
|
||||
this.clientB.on("connectionAccepted", cb);
|
||||
|
||||
@clientBDisconnectMessages = []
|
||||
@clientB.on "clientTracking.clientDisconnected", (data) =>
|
||||
@clientBDisconnectMessages.push data
|
||||
this.clientBDisconnectMessages = [];
|
||||
return this.clientB.on("clientTracking.clientDisconnected", data => {
|
||||
return this.clientBDisconnectMessages.push(data);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientB.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
(cb) =>
|
||||
@clientB.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
cb => {
|
||||
return this.clientB.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# leaveProject is called when the client disconnects
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
cb => {
|
||||
// leaveProject is called when the client disconnects
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# The API waits a little while before flushing changes
|
||||
setTimeout done, 1000
|
||||
cb => {
|
||||
// The API waits a little while before flushing changes
|
||||
return setTimeout(done, 1000);
|
||||
}
|
||||
|
||||
], done
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should emit a disconnect message to the room", ->
|
||||
@clientBDisconnectMessages.should.deep.equal [@clientA.publicId]
|
||||
it("should emit a disconnect message to the room", function() {
|
||||
return this.clientBDisconnectMessages.should.deep.equal([this.clientA.publicId]);
|
||||
});
|
||||
|
||||
it "should no longer list the client in connected users", (done) ->
|
||||
@clientB.emit "clientTracking.getConnectedUsers", (error, users) =>
|
||||
for user in users
|
||||
if user.client_id == @clientA.publicId
|
||||
throw "Expected clientA to not be listed in connected users"
|
||||
return done()
|
||||
it("should no longer list the client in connected users", function(done) {
|
||||
return this.clientB.emit("clientTracking.getConnectedUsers", (error, users) => {
|
||||
for (let user of Array.from(users)) {
|
||||
if (user.client_id === this.clientA.publicId) {
|
||||
throw "Expected clientA to not be listed in connected users";
|
||||
}
|
||||
}
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should not flush the project to the document updater", ->
|
||||
MockDocUpdaterServer.deleteProject
|
||||
.calledWith(@project_id)
|
||||
.should.equal false
|
||||
it("should not flush the project to the document updater", function() {
|
||||
return MockDocUpdaterServer.deleteProject
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(false);
|
||||
});
|
||||
|
||||
it "should remain subscribed to the editor-events channels", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.include "editor-events:#{@project_id}"
|
||||
done()
|
||||
return null
|
||||
it("should remain subscribed to the editor-events channels", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.include(`editor-events:${this.project_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should remain subscribed to the applied-ops channels", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should remain subscribed to the applied-ops channels", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "with no other clients in the project", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
return describe("with no other clients in the project", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connect", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connect", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
cb => {
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# leaveProject is called when the client disconnects
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
cb => {
|
||||
// leaveProject is called when the client disconnects
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# The API waits a little while before flushing changes
|
||||
setTimeout done, 1000
|
||||
], done
|
||||
cb => {
|
||||
// The API waits a little while before flushing changes
|
||||
return setTimeout(done, 1000);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should flush the project to the document updater", ->
|
||||
MockDocUpdaterServer.deleteProject
|
||||
.calledWith(@project_id)
|
||||
.should.equal true
|
||||
it("should flush the project to the document updater", function() {
|
||||
return MockDocUpdaterServer.deleteProject
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should not subscribe to the editor-events channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "editor-events:#{@project_id}"
|
||||
done()
|
||||
return null
|
||||
it("should not subscribe to the editor-events channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`editor-events:${this.project_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not subscribe to the applied-ops channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should not subscribe to the applied-ops channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,205 +1,277 @@
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(settings.redis.pubsub)
|
||||
const settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(settings.redis.pubsub);
|
||||
|
||||
describe "PubSubRace", ->
|
||||
before (done) ->
|
||||
MockDocUpdaterServer.run done
|
||||
describe("PubSubRace", function() {
|
||||
before(done => MockDocUpdaterServer.run(done));
|
||||
|
||||
describe "when the client leaves a doc before joinDoc completes", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("when the client leaves a doc before joinDoc completes", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connect", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connect", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
# leave before joinDoc completes
|
||||
@clientA.emit "leaveDoc", @doc_id, cb
|
||||
cb => {
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
// leave before joinDoc completes
|
||||
return this.clientA.emit("leaveDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for subscribe and unsubscribe
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for subscribe and unsubscribe
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should not subscribe to the applied-ops channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should not subscribe to the applied-ops channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the client emits joinDoc and leaveDoc requests frequently and leaves eventually", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("when the client emits joinDoc and leaveDoc requests frequently and leaves eventually", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connect", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connect", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, cb
|
||||
cb => {
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
return this.clientA.emit("leaveDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for subscribe and unsubscribe
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for subscribe and unsubscribe
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should not subscribe to the applied-ops channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should not subscribe to the applied-ops channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the client emits joinDoc and leaveDoc requests frequently and remains in the doc", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
describe("when the client emits joinDoc and leaveDoc requests frequently and remains in the doc", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connect", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connect", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
@clientA.emit "leaveDoc", @doc_id, () ->
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("joinDoc", this.doc_id, function() {});
|
||||
this.clientA.emit("leaveDoc", this.doc_id, function() {});
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for subscribe and unsubscribe
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for subscribe and unsubscribe
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should subscribe to the applied-ops channels", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should subscribe to the applied-ops channels", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the client disconnects before joinDoc completes", ->
|
||||
before (done) ->
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
return describe("when the client disconnects before joinDoc completes", function() {
|
||||
before(function(done) {
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) => cb()
|
||||
}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connect", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connect", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
cb(error)
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
|
||||
this.project = project;
|
||||
this.privilegeLevel = privilegeLevel;
|
||||
this.protocolVersion = protocolVersion;
|
||||
return cb(error);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
joinDocCompleted = false
|
||||
@clientA.emit "joinDoc", @doc_id, () ->
|
||||
joinDocCompleted = true
|
||||
# leave before joinDoc completes
|
||||
setTimeout () =>
|
||||
if joinDocCompleted
|
||||
return cb(new Error('joinDocCompleted -- lower timeout'))
|
||||
@clientA.on "disconnect", () -> cb()
|
||||
@clientA.disconnect()
|
||||
# socket.io processes joinDoc and disconnect with different delays:
|
||||
# - joinDoc goes through two process.nextTick
|
||||
# - disconnect goes through one process.nextTick
|
||||
# We have to inject the disconnect event into a different event loop
|
||||
# cycle.
|
||||
, 3
|
||||
cb => {
|
||||
let joinDocCompleted = false;
|
||||
this.clientA.emit("joinDoc", this.doc_id, () => joinDocCompleted = true);
|
||||
// leave before joinDoc completes
|
||||
return setTimeout(() => {
|
||||
if (joinDocCompleted) {
|
||||
return cb(new Error('joinDocCompleted -- lower timeout'));
|
||||
}
|
||||
this.clientA.on("disconnect", () => cb());
|
||||
return this.clientA.disconnect();
|
||||
}
|
||||
// socket.io processes joinDoc and disconnect with different delays:
|
||||
// - joinDoc goes through two process.nextTick
|
||||
// - disconnect goes through one process.nextTick
|
||||
// We have to inject the disconnect event into a different event loop
|
||||
// cycle.
|
||||
, 3);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
# wait for subscribe and unsubscribe
|
||||
setTimeout cb, 100
|
||||
], done
|
||||
cb => {
|
||||
// wait for subscribe and unsubscribe
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should not subscribe to the editor-events channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "editor-events:#{@project_id}"
|
||||
done()
|
||||
return null
|
||||
it("should not subscribe to the editor-events channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`editor-events:${this.project_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not subscribe to the applied-ops channels anymore", (done) ->
|
||||
rclient.pubsub 'CHANNELS', (err, resp) =>
|
||||
return done(err) if err
|
||||
resp.should.not.include "applied-ops:#{@doc_id}"
|
||||
done()
|
||||
return null
|
||||
return it("should not subscribe to the applied-ops channels anymore", function(done) {
|
||||
rclient.pubsub('CHANNELS', (err, resp) => {
|
||||
if (err) { return done(err); }
|
||||
resp.should.not.include(`applied-ops:${this.doc_id}`);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,208 +1,274 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
chai.should();
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockWebServer = require "./helpers/MockWebServer"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const MockWebServer = require("./helpers/MockWebServer");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
async = require "async"
|
||||
const async = require("async");
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(settings.redis.pubsub)
|
||||
const settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(settings.redis.pubsub);
|
||||
|
||||
describe "receiveUpdate", ->
|
||||
beforeEach (done) ->
|
||||
@lines = ["test", "doc", "lines"]
|
||||
@version = 42
|
||||
@ops = ["mock", "doc", "ops"]
|
||||
describe("receiveUpdate", function() {
|
||||
beforeEach(function(done) {
|
||||
this.lines = ["test", "doc", "lines"];
|
||||
this.version = 42;
|
||||
this.ops = ["mock", "doc", "ops"];
|
||||
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: { name: "Test Project" }
|
||||
}, (error, {@user_id, @project_id}) => cb()
|
||||
}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
|
||||
this.doc_id = doc_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA = RealTimeClient.connect()
|
||||
@clientA.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientA = RealTimeClient.connect();
|
||||
return this.clientA.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB = RealTimeClient.connect()
|
||||
@clientB.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientB = RealTimeClient.connect();
|
||||
return this.clientB.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientA.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.clientA.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, cb
|
||||
cb => {
|
||||
return this.clientB.emit("joinProject", {
|
||||
project_id: this.project_id
|
||||
}, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientB.emit "joinDoc", @doc_id, cb
|
||||
cb => {
|
||||
return this.clientB.emit("joinDoc", this.doc_id, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {name: "Test Project"}
|
||||
}, (error, {user_id: @user_id_second, project_id: @project_id_second}) => cb()
|
||||
}, (error, {user_id: user_id_second, project_id: project_id_second}) => { this.user_id_second = user_id_second; this.project_id_second = project_id_second; return cb(); });
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
FixturesManager.setUpDoc @project_id_second, {@lines, @version, @ops}, (e, {doc_id: @doc_id_second}) =>
|
||||
cb(e)
|
||||
cb => {
|
||||
return FixturesManager.setUpDoc(this.project_id_second, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id: doc_id_second}) => {
|
||||
this.doc_id_second = doc_id_second;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientC = RealTimeClient.connect()
|
||||
@clientC.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.clientC = RealTimeClient.connect();
|
||||
return this.clientC.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientC.emit "joinProject", {
|
||||
project_id: @project_id_second
|
||||
}, cb
|
||||
(cb) =>
|
||||
@clientC.emit "joinDoc", @doc_id_second, cb
|
||||
cb => {
|
||||
return this.clientC.emit("joinProject", {
|
||||
project_id: this.project_id_second
|
||||
}, cb);
|
||||
},
|
||||
cb => {
|
||||
return this.clientC.emit("joinDoc", this.doc_id_second, cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@clientAUpdates = []
|
||||
@clientA.on "otUpdateApplied", (update) => @clientAUpdates.push(update)
|
||||
@clientBUpdates = []
|
||||
@clientB.on "otUpdateApplied", (update) => @clientBUpdates.push(update)
|
||||
@clientCUpdates = []
|
||||
@clientC.on "otUpdateApplied", (update) => @clientCUpdates.push(update)
|
||||
cb => {
|
||||
this.clientAUpdates = [];
|
||||
this.clientA.on("otUpdateApplied", update => this.clientAUpdates.push(update));
|
||||
this.clientBUpdates = [];
|
||||
this.clientB.on("otUpdateApplied", update => this.clientBUpdates.push(update));
|
||||
this.clientCUpdates = [];
|
||||
this.clientC.on("otUpdateApplied", update => this.clientCUpdates.push(update));
|
||||
|
||||
@clientAErrors = []
|
||||
@clientA.on "otUpdateError", (error) => @clientAErrors.push(error)
|
||||
@clientBErrors = []
|
||||
@clientB.on "otUpdateError", (error) => @clientBErrors.push(error)
|
||||
@clientCErrors = []
|
||||
@clientC.on "otUpdateError", (error) => @clientCErrors.push(error)
|
||||
cb()
|
||||
], done
|
||||
|
||||
afterEach () ->
|
||||
@clientA?.disconnect()
|
||||
@clientB?.disconnect()
|
||||
@clientC?.disconnect()
|
||||
|
||||
describe "with an update from clientA", ->
|
||||
beforeEach (done) ->
|
||||
@update = {
|
||||
doc_id: @doc_id
|
||||
op:
|
||||
meta:
|
||||
source: @clientA.publicId
|
||||
v: @version
|
||||
doc: @doc_id
|
||||
op: [{i: "foo", p: 50}]
|
||||
this.clientAErrors = [];
|
||||
this.clientA.on("otUpdateError", error => this.clientAErrors.push(error));
|
||||
this.clientBErrors = [];
|
||||
this.clientB.on("otUpdateError", error => this.clientBErrors.push(error));
|
||||
this.clientCErrors = [];
|
||||
this.clientC.on("otUpdateError", error => this.clientCErrors.push(error));
|
||||
return cb();
|
||||
}
|
||||
rclient.publish "applied-ops", JSON.stringify(@update)
|
||||
setTimeout done, 200 # Give clients time to get message
|
||||
|
||||
it "should send the full op to clientB", ->
|
||||
@clientBUpdates.should.deep.equal [@update.op]
|
||||
|
||||
it "should send an ack to clientA", ->
|
||||
@clientAUpdates.should.deep.equal [{
|
||||
v: @version, doc: @doc_id
|
||||
}]
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should send nothing to clientC", ->
|
||||
@clientCUpdates.should.deep.equal []
|
||||
afterEach(function() {
|
||||
if (this.clientA != null) {
|
||||
this.clientA.disconnect();
|
||||
}
|
||||
if (this.clientB != null) {
|
||||
this.clientB.disconnect();
|
||||
}
|
||||
return (this.clientC != null ? this.clientC.disconnect() : undefined);
|
||||
});
|
||||
|
||||
describe "with an update from clientC", ->
|
||||
beforeEach (done) ->
|
||||
@update = {
|
||||
doc_id: @doc_id_second
|
||||
op:
|
||||
meta:
|
||||
source: @clientC.publicId
|
||||
v: @version
|
||||
doc: @doc_id_second
|
||||
op: [{i: "update from clientC", p: 50}]
|
||||
}
|
||||
rclient.publish "applied-ops", JSON.stringify(@update)
|
||||
setTimeout done, 200 # Give clients time to get message
|
||||
|
||||
it "should send nothing to clientA", ->
|
||||
@clientAUpdates.should.deep.equal []
|
||||
|
||||
it "should send nothing to clientB", ->
|
||||
@clientBUpdates.should.deep.equal []
|
||||
|
||||
it "should send an ack to clientC", ->
|
||||
@clientCUpdates.should.deep.equal [{
|
||||
v: @version, doc: @doc_id_second
|
||||
}]
|
||||
|
||||
describe "with an update from a remote client for project 1", ->
|
||||
beforeEach (done) ->
|
||||
@update = {
|
||||
doc_id: @doc_id
|
||||
op:
|
||||
meta:
|
||||
source: 'this-is-a-remote-client-id'
|
||||
v: @version
|
||||
doc: @doc_id
|
||||
describe("with an update from clientA", function() {
|
||||
beforeEach(function(done) {
|
||||
this.update = {
|
||||
doc_id: this.doc_id,
|
||||
op: {
|
||||
meta: {
|
||||
source: this.clientA.publicId
|
||||
},
|
||||
v: this.version,
|
||||
doc: this.doc_id,
|
||||
op: [{i: "foo", p: 50}]
|
||||
}
|
||||
rclient.publish "applied-ops", JSON.stringify(@update)
|
||||
setTimeout done, 200 # Give clients time to get message
|
||||
|
||||
it "should send the full op to clientA", ->
|
||||
@clientAUpdates.should.deep.equal [@update.op]
|
||||
}
|
||||
};
|
||||
rclient.publish("applied-ops", JSON.stringify(this.update));
|
||||
return setTimeout(done, 200);
|
||||
}); // Give clients time to get message
|
||||
|
||||
it "should send the full op to clientB", ->
|
||||
@clientBUpdates.should.deep.equal [@update.op]
|
||||
it("should send the full op to clientB", function() {
|
||||
return this.clientBUpdates.should.deep.equal([this.update.op]);
|
||||
});
|
||||
|
||||
it("should send an ack to clientA", function() {
|
||||
return this.clientAUpdates.should.deep.equal([{
|
||||
v: this.version, doc: this.doc_id
|
||||
}]);
|
||||
});
|
||||
|
||||
it "should send nothing to clientC", ->
|
||||
@clientCUpdates.should.deep.equal []
|
||||
return it("should send nothing to clientC", function() {
|
||||
return this.clientCUpdates.should.deep.equal([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe "with an error for the first project", ->
|
||||
beforeEach (done) ->
|
||||
rclient.publish "applied-ops", JSON.stringify({doc_id: @doc_id, error: @error = "something went wrong"})
|
||||
setTimeout done, 200 # Give clients time to get message
|
||||
describe("with an update from clientC", function() {
|
||||
beforeEach(function(done) {
|
||||
this.update = {
|
||||
doc_id: this.doc_id_second,
|
||||
op: {
|
||||
meta: {
|
||||
source: this.clientC.publicId
|
||||
},
|
||||
v: this.version,
|
||||
doc: this.doc_id_second,
|
||||
op: [{i: "update from clientC", p: 50}]
|
||||
}
|
||||
};
|
||||
rclient.publish("applied-ops", JSON.stringify(this.update));
|
||||
return setTimeout(done, 200);
|
||||
}); // Give clients time to get message
|
||||
|
||||
it "should send the error to the clients in the first project", ->
|
||||
@clientAErrors.should.deep.equal [@error]
|
||||
@clientBErrors.should.deep.equal [@error]
|
||||
it("should send nothing to clientA", function() {
|
||||
return this.clientAUpdates.should.deep.equal([]);
|
||||
});
|
||||
|
||||
it "should not send any errors to the client in the second project", ->
|
||||
@clientCErrors.should.deep.equal []
|
||||
it("should send nothing to clientB", function() {
|
||||
return this.clientBUpdates.should.deep.equal([]);
|
||||
});
|
||||
|
||||
it "should disconnect the clients of the first project", ->
|
||||
@clientA.socket.connected.should.equal false
|
||||
@clientB.socket.connected.should.equal false
|
||||
return it("should send an ack to clientC", function() {
|
||||
return this.clientCUpdates.should.deep.equal([{
|
||||
v: this.version, doc: this.doc_id_second
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
it "should not disconnect the client in the second project", ->
|
||||
@clientC.socket.connected.should.equal true
|
||||
describe("with an update from a remote client for project 1", function() {
|
||||
beforeEach(function(done) {
|
||||
this.update = {
|
||||
doc_id: this.doc_id,
|
||||
op: {
|
||||
meta: {
|
||||
source: 'this-is-a-remote-client-id'
|
||||
},
|
||||
v: this.version,
|
||||
doc: this.doc_id,
|
||||
op: [{i: "foo", p: 50}]
|
||||
}
|
||||
};
|
||||
rclient.publish("applied-ops", JSON.stringify(this.update));
|
||||
return setTimeout(done, 200);
|
||||
}); // Give clients time to get message
|
||||
|
||||
describe "with an error for the second project", ->
|
||||
beforeEach (done) ->
|
||||
rclient.publish "applied-ops", JSON.stringify({doc_id: @doc_id_second, error: @error = "something went wrong"})
|
||||
setTimeout done, 200 # Give clients time to get message
|
||||
it("should send the full op to clientA", function() {
|
||||
return this.clientAUpdates.should.deep.equal([this.update.op]);
|
||||
});
|
||||
|
||||
it("should send the full op to clientB", function() {
|
||||
return this.clientBUpdates.should.deep.equal([this.update.op]);
|
||||
});
|
||||
|
||||
it "should not send any errors to the clients in the first project", ->
|
||||
@clientAErrors.should.deep.equal []
|
||||
@clientBErrors.should.deep.equal []
|
||||
return it("should send nothing to clientC", function() {
|
||||
return this.clientCUpdates.should.deep.equal([]);
|
||||
});
|
||||
});
|
||||
|
||||
it "should send the error to the client in the second project", ->
|
||||
@clientCErrors.should.deep.equal [@error]
|
||||
describe("with an error for the first project", function() {
|
||||
beforeEach(function(done) {
|
||||
rclient.publish("applied-ops", JSON.stringify({doc_id: this.doc_id, error: (this.error = "something went wrong")}));
|
||||
return setTimeout(done, 200);
|
||||
}); // Give clients time to get message
|
||||
|
||||
it "should not disconnect the clients of the first project", ->
|
||||
@clientA.socket.connected.should.equal true
|
||||
@clientB.socket.connected.should.equal true
|
||||
it("should send the error to the clients in the first project", function() {
|
||||
this.clientAErrors.should.deep.equal([this.error]);
|
||||
return this.clientBErrors.should.deep.equal([this.error]);
|
||||
});
|
||||
|
||||
it "should disconnect the client in the second project", ->
|
||||
@clientC.socket.connected.should.equal false
|
||||
it("should not send any errors to the client in the second project", function() {
|
||||
return this.clientCErrors.should.deep.equal([]);
|
||||
});
|
||||
|
||||
it("should disconnect the clients of the first project", function() {
|
||||
this.clientA.socket.connected.should.equal(false);
|
||||
return this.clientB.socket.connected.should.equal(false);
|
||||
});
|
||||
|
||||
return it("should not disconnect the client in the second project", function() {
|
||||
return this.clientC.socket.connected.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
return describe("with an error for the second project", function() {
|
||||
beforeEach(function(done) {
|
||||
rclient.publish("applied-ops", JSON.stringify({doc_id: this.doc_id_second, error: (this.error = "something went wrong")}));
|
||||
return setTimeout(done, 200);
|
||||
}); // Give clients time to get message
|
||||
|
||||
it("should not send any errors to the clients in the first project", function() {
|
||||
this.clientAErrors.should.deep.equal([]);
|
||||
return this.clientBErrors.should.deep.equal([]);
|
||||
});
|
||||
|
||||
it("should send the error to the client in the second project", function() {
|
||||
return this.clientCErrors.should.deep.equal([this.error]);
|
||||
});
|
||||
|
||||
it("should not disconnect the clients of the first project", function() {
|
||||
this.clientA.socket.connected.should.equal(true);
|
||||
return this.clientB.socket.connected.should.equal(true);
|
||||
});
|
||||
|
||||
return it("should disconnect the client in the second project", function() {
|
||||
return this.clientC.socket.connected.should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,76 +1,101 @@
|
||||
async = require "async"
|
||||
{expect} = require("chai")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const async = require("async");
|
||||
const {expect} = require("chai");
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
FixturesManager = require "./helpers/FixturesManager"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const FixturesManager = require("./helpers/FixturesManager");
|
||||
|
||||
|
||||
describe "Router", ->
|
||||
describe "joinProject", ->
|
||||
describe "when there is no callback provided", ->
|
||||
after () ->
|
||||
process.removeListener('unhandledRejection', @onUnhandled)
|
||||
describe("Router", () => describe("joinProject", function() {
|
||||
describe("when there is no callback provided", function() {
|
||||
after(function() {
|
||||
return process.removeListener('unhandledRejection', this.onUnhandled);
|
||||
});
|
||||
|
||||
before (done) ->
|
||||
@onUnhandled = (error) ->
|
||||
done(error)
|
||||
process.on('unhandledRejection', @onUnhandled)
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
before(function(done) {
|
||||
this.onUnhandled = error => done(error);
|
||||
process.on('unhandledRejection', this.onUnhandled);
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", project_id: @project_id
|
||||
setTimeout(cb, 100)
|
||||
], done
|
||||
cb => {
|
||||
this.client.emit("joinProject", {project_id: this.project_id});
|
||||
return setTimeout(cb, 100);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should keep on going", ->
|
||||
expect('still running').to.exist
|
||||
return it("should keep on going", () => expect('still running').to.exist);
|
||||
});
|
||||
|
||||
describe "when there are too many arguments", ->
|
||||
after () ->
|
||||
process.removeListener('unhandledRejection', @onUnhandled)
|
||||
return describe("when there are too many arguments", function() {
|
||||
after(function() {
|
||||
return process.removeListener('unhandledRejection', this.onUnhandled);
|
||||
});
|
||||
|
||||
before (done) ->
|
||||
@onUnhandled = (error) ->
|
||||
done(error)
|
||||
process.on('unhandledRejection', @onUnhandled)
|
||||
async.series [
|
||||
(cb) =>
|
||||
FixturesManager.setUpProject {
|
||||
privilegeLevel: "owner"
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {@project_id, @user_id}) =>
|
||||
cb(e)
|
||||
before(function(done) {
|
||||
this.onUnhandled = error => done(error);
|
||||
process.on('unhandledRejection', this.onUnhandled);
|
||||
return async.series([
|
||||
cb => {
|
||||
return FixturesManager.setUpProject({
|
||||
privilegeLevel: "owner",
|
||||
project: {
|
||||
name: "Test Project"
|
||||
}
|
||||
}, (e, {project_id, user_id}) => {
|
||||
this.project_id = project_id;
|
||||
this.user_id = user_id;
|
||||
return cb(e);
|
||||
});
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client = RealTimeClient.connect()
|
||||
@client.on "connectionAccepted", cb
|
||||
cb => {
|
||||
this.client = RealTimeClient.connect();
|
||||
return this.client.on("connectionAccepted", cb);
|
||||
},
|
||||
|
||||
(cb) =>
|
||||
@client.emit "joinProject", 1, 2, 3, 4, 5, (@error) =>
|
||||
cb()
|
||||
], done
|
||||
cb => {
|
||||
return this.client.emit("joinProject", 1, 2, 3, 4, 5, error => {
|
||||
this.error = error;
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
it "should return an error message", ->
|
||||
expect(@error.message).to.equal('unexpected arguments')
|
||||
return it("should return an error message", function() {
|
||||
return expect(this.error.message).to.equal('unexpected arguments');
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -1,67 +1,90 @@
|
||||
RealTimeClient = require("./helpers/RealTimeClient")
|
||||
Settings = require("settings-sharelatex")
|
||||
{expect} = require('chai')
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
const Settings = require("settings-sharelatex");
|
||||
const {expect} = require('chai');
|
||||
|
||||
describe 'SessionSockets', ->
|
||||
before ->
|
||||
@checkSocket = (fn) ->
|
||||
client = RealTimeClient.connect()
|
||||
client.on 'connectionAccepted', fn
|
||||
client.on 'connectionRejected', fn
|
||||
return null
|
||||
describe('SessionSockets', function() {
|
||||
before(function() {
|
||||
return this.checkSocket = function(fn) {
|
||||
const client = RealTimeClient.connect();
|
||||
client.on('connectionAccepted', fn);
|
||||
client.on('connectionRejected', fn);
|
||||
return null;
|
||||
};
|
||||
});
|
||||
|
||||
describe 'without cookies', ->
|
||||
before ->
|
||||
RealTimeClient.cookie = null
|
||||
describe('without cookies', function() {
|
||||
before(() => RealTimeClient.cookie = null);
|
||||
|
||||
it 'should return a lookup error', (done) ->
|
||||
@checkSocket (error) ->
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('invalid session')
|
||||
done()
|
||||
return it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('invalid session');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe 'with a different cookie', ->
|
||||
before ->
|
||||
RealTimeClient.cookie = "some.key=someValue"
|
||||
describe('with a different cookie', function() {
|
||||
before(() => RealTimeClient.cookie = "some.key=someValue");
|
||||
|
||||
it 'should return a lookup error', (done) ->
|
||||
@checkSocket (error) ->
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('invalid session')
|
||||
done()
|
||||
return it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('invalid session');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe 'with an invalid cookie', ->
|
||||
before (done) ->
|
||||
RealTimeClient.setSession {}, (error) ->
|
||||
return done(error) if error
|
||||
RealTimeClient.cookie = "#{Settings.cookieName}=#{
|
||||
describe('with an invalid cookie', function() {
|
||||
before(function(done) {
|
||||
RealTimeClient.setSession({}, function(error) {
|
||||
if (error) { return done(error); }
|
||||
RealTimeClient.cookie = `${Settings.cookieName}=${
|
||||
RealTimeClient.cookie.slice(17, 49)
|
||||
}"
|
||||
done()
|
||||
return null
|
||||
}`;
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it 'should return a lookup error', (done) ->
|
||||
@checkSocket (error) ->
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('invalid session')
|
||||
done()
|
||||
return it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('invalid session');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe 'with a valid cookie and no matching session', ->
|
||||
before ->
|
||||
RealTimeClient.cookie = "#{Settings.cookieName}=unknownId"
|
||||
describe('with a valid cookie and no matching session', function() {
|
||||
before(() => RealTimeClient.cookie = `${Settings.cookieName}=unknownId`);
|
||||
|
||||
it 'should return a lookup error', (done) ->
|
||||
@checkSocket (error) ->
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('invalid session')
|
||||
done()
|
||||
return it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(function(error) {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('invalid session');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe 'with a valid cookie and a matching session', ->
|
||||
before (done) ->
|
||||
RealTimeClient.setSession({}, done)
|
||||
return null
|
||||
return describe('with a valid cookie and a matching session', function() {
|
||||
before(function(done) {
|
||||
RealTimeClient.setSession({}, done);
|
||||
return null;
|
||||
});
|
||||
|
||||
it 'should not return an error', (done) ->
|
||||
@checkSocket (error) ->
|
||||
expect(error).to.not.exist
|
||||
done()
|
||||
return it('should not return an error', function(done) {
|
||||
return this.checkSocket(function(error) {
|
||||
expect(error).to.not.exist;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,51 @@
|
||||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
const RealTimeClient = require("./helpers/RealTimeClient");
|
||||
|
||||
describe "Session", ->
|
||||
describe "with an established session", ->
|
||||
before (done) ->
|
||||
@user_id = "mock-user-id"
|
||||
RealTimeClient.setSession {
|
||||
user: { _id: @user_id }
|
||||
}, (error) =>
|
||||
throw error if error?
|
||||
@client = RealTimeClient.connect()
|
||||
return done()
|
||||
return null
|
||||
describe("Session", () => describe("with an established session", function() {
|
||||
before(function(done) {
|
||||
this.user_id = "mock-user-id";
|
||||
RealTimeClient.setSession({
|
||||
user: { _id: this.user_id }
|
||||
}, error => {
|
||||
if (error != null) { throw error; }
|
||||
this.client = RealTimeClient.connect();
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not get disconnected", (done) ->
|
||||
disconnected = false
|
||||
@client.on "disconnect", () ->
|
||||
disconnected = true
|
||||
setTimeout () =>
|
||||
expect(disconnected).to.equal false
|
||||
done()
|
||||
, 500
|
||||
|
||||
it "should appear in the list of connected clients", (done) ->
|
||||
RealTimeClient.getConnectedClients (error, clients) =>
|
||||
included = false
|
||||
for client in clients
|
||||
if client.client_id == @client.socket.sessionid
|
||||
included = true
|
||||
break
|
||||
expect(included).to.equal true
|
||||
done()
|
||||
it("should not get disconnected", function(done) {
|
||||
let disconnected = false;
|
||||
this.client.on("disconnect", () => disconnected = true);
|
||||
return setTimeout(() => {
|
||||
expect(disconnected).to.equal(false);
|
||||
return done();
|
||||
}
|
||||
, 500);
|
||||
});
|
||||
|
||||
return it("should appear in the list of connected clients", function(done) {
|
||||
return RealTimeClient.getConnectedClients((error, clients) => {
|
||||
let included = false;
|
||||
for (let client of Array.from(clients)) {
|
||||
if (client.client_id === this.client.socket.sessionid) {
|
||||
included = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(included).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -1,48 +1,67 @@
|
||||
RealTimeClient = require "./RealTimeClient"
|
||||
MockWebServer = require "./MockWebServer"
|
||||
MockDocUpdaterServer = require "./MockDocUpdaterServer"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let FixturesManager;
|
||||
const RealTimeClient = require("./RealTimeClient");
|
||||
const MockWebServer = require("./MockWebServer");
|
||||
const MockDocUpdaterServer = require("./MockDocUpdaterServer");
|
||||
|
||||
module.exports = FixturesManager =
|
||||
setUpProject: (options = {}, callback = (error, data) ->) ->
|
||||
options.user_id ||= FixturesManager.getRandomId()
|
||||
options.project_id ||= FixturesManager.getRandomId()
|
||||
options.project ||= { name: "Test Project" }
|
||||
{project_id, user_id, privilegeLevel, project, publicAccess} = options
|
||||
module.exports = (FixturesManager = {
|
||||
setUpProject(options, callback) {
|
||||
if (options == null) { options = {}; }
|
||||
if (callback == null) { callback = function(error, data) {}; }
|
||||
if (!options.user_id) { options.user_id = FixturesManager.getRandomId(); }
|
||||
if (!options.project_id) { options.project_id = FixturesManager.getRandomId(); }
|
||||
if (!options.project) { options.project = { name: "Test Project" }; }
|
||||
const {project_id, user_id, privilegeLevel, project, publicAccess} = options;
|
||||
|
||||
privileges = {}
|
||||
privileges[user_id] = privilegeLevel
|
||||
if publicAccess
|
||||
privileges["anonymous-user"] = publicAccess
|
||||
const privileges = {};
|
||||
privileges[user_id] = privilegeLevel;
|
||||
if (publicAccess) {
|
||||
privileges["anonymous-user"] = publicAccess;
|
||||
}
|
||||
|
||||
MockWebServer.createMockProject(project_id, privileges, project)
|
||||
MockWebServer.run (error) =>
|
||||
throw error if error?
|
||||
RealTimeClient.setSession {
|
||||
MockWebServer.createMockProject(project_id, privileges, project);
|
||||
return MockWebServer.run(error => {
|
||||
if (error != null) { throw error; }
|
||||
return RealTimeClient.setSession({
|
||||
user: {
|
||||
_id: user_id
|
||||
first_name: "Joe"
|
||||
_id: user_id,
|
||||
first_name: "Joe",
|
||||
last_name: "Bloggs"
|
||||
}
|
||||
}, (error) =>
|
||||
throw error if error?
|
||||
callback null, {project_id, user_id, privilegeLevel, project}
|
||||
}, error => {
|
||||
if (error != null) { throw error; }
|
||||
return callback(null, {project_id, user_id, privilegeLevel, project});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setUpDoc: (project_id, options = {}, callback = (error, data) ->) ->
|
||||
options.doc_id ||= FixturesManager.getRandomId()
|
||||
options.lines ||= ["doc", "lines"]
|
||||
options.version ||= 42
|
||||
options.ops ||= ["mock", "ops"]
|
||||
{doc_id, lines, version, ops, ranges} = options
|
||||
setUpDoc(project_id, options, callback) {
|
||||
if (options == null) { options = {}; }
|
||||
if (callback == null) { callback = function(error, data) {}; }
|
||||
if (!options.doc_id) { options.doc_id = FixturesManager.getRandomId(); }
|
||||
if (!options.lines) { options.lines = ["doc", "lines"]; }
|
||||
if (!options.version) { options.version = 42; }
|
||||
if (!options.ops) { options.ops = ["mock", "ops"]; }
|
||||
const {doc_id, lines, version, ops, ranges} = options;
|
||||
|
||||
MockDocUpdaterServer.createMockDoc project_id, doc_id, {lines, version, ops, ranges}
|
||||
MockDocUpdaterServer.run (error) =>
|
||||
throw error if error?
|
||||
callback null, {project_id, doc_id, lines, version, ops}
|
||||
MockDocUpdaterServer.createMockDoc(project_id, doc_id, {lines, version, ops, ranges});
|
||||
return MockDocUpdaterServer.run(error => {
|
||||
if (error != null) { throw error; }
|
||||
return callback(null, {project_id, doc_id, lines, version, ops});
|
||||
});
|
||||
},
|
||||
|
||||
getRandomId: () ->
|
||||
getRandomId() {
|
||||
return require("crypto")
|
||||
.createHash("sha1")
|
||||
.update(Math.random().toString())
|
||||
.digest("hex")
|
||||
.slice(0,24)
|
||||
.slice(0,24);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,46 +1,65 @@
|
||||
sinon = require "sinon"
|
||||
express = require "express"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let MockDocUpdaterServer;
|
||||
const sinon = require("sinon");
|
||||
const express = require("express");
|
||||
|
||||
module.exports = MockDocUpdaterServer =
|
||||
docs: {}
|
||||
module.exports = (MockDocUpdaterServer = {
|
||||
docs: {},
|
||||
|
||||
createMockDoc: (project_id, doc_id, data) ->
|
||||
MockDocUpdaterServer.docs["#{project_id}:#{doc_id}"] = data
|
||||
createMockDoc(project_id, doc_id, data) {
|
||||
return MockDocUpdaterServer.docs[`${project_id}:${doc_id}`] = data;
|
||||
},
|
||||
|
||||
getDocument: (project_id, doc_id, fromVersion, callback = (error, data) ->) ->
|
||||
callback(
|
||||
null, MockDocUpdaterServer.docs["#{project_id}:#{doc_id}"]
|
||||
)
|
||||
getDocument(project_id, doc_id, fromVersion, callback) {
|
||||
if (callback == null) { callback = function(error, data) {}; }
|
||||
return callback(
|
||||
null, MockDocUpdaterServer.docs[`${project_id}:${doc_id}`]
|
||||
);
|
||||
},
|
||||
|
||||
deleteProject: sinon.stub().callsArg(1)
|
||||
deleteProject: sinon.stub().callsArg(1),
|
||||
|
||||
getDocumentRequest: (req, res, next) ->
|
||||
{project_id, doc_id} = req.params
|
||||
{fromVersion} = req.query
|
||||
fromVersion = parseInt(fromVersion, 10)
|
||||
MockDocUpdaterServer.getDocument project_id, doc_id, fromVersion, (error, data) ->
|
||||
return next(error) if error?
|
||||
res.json data
|
||||
getDocumentRequest(req, res, next) {
|
||||
const {project_id, doc_id} = req.params;
|
||||
let {fromVersion} = req.query;
|
||||
fromVersion = parseInt(fromVersion, 10);
|
||||
return MockDocUpdaterServer.getDocument(project_id, doc_id, fromVersion, function(error, data) {
|
||||
if (error != null) { return next(error); }
|
||||
return res.json(data);
|
||||
});
|
||||
},
|
||||
|
||||
deleteProjectRequest: (req, res, next) ->
|
||||
{project_id} = req.params
|
||||
MockDocUpdaterServer.deleteProject project_id, (error) ->
|
||||
return next(error) if error?
|
||||
res.sendStatus 204
|
||||
deleteProjectRequest(req, res, next) {
|
||||
const {project_id} = req.params;
|
||||
return MockDocUpdaterServer.deleteProject(project_id, function(error) {
|
||||
if (error != null) { return next(error); }
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
},
|
||||
|
||||
running: false
|
||||
run: (callback = (error) ->) ->
|
||||
if MockDocUpdaterServer.running
|
||||
return callback()
|
||||
app = express()
|
||||
app.get "/project/:project_id/doc/:doc_id", MockDocUpdaterServer.getDocumentRequest
|
||||
app.delete "/project/:project_id", MockDocUpdaterServer.deleteProjectRequest
|
||||
app.listen 3003, (error) ->
|
||||
MockDocUpdaterServer.running = true
|
||||
callback(error)
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockDocUpdaterServer:", error.message
|
||||
process.exit(1)
|
||||
running: false,
|
||||
run(callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
if (MockDocUpdaterServer.running) {
|
||||
return callback();
|
||||
}
|
||||
const app = express();
|
||||
app.get("/project/:project_id/doc/:doc_id", MockDocUpdaterServer.getDocumentRequest);
|
||||
app.delete("/project/:project_id", MockDocUpdaterServer.deleteProjectRequest);
|
||||
return app.listen(3003, function(error) {
|
||||
MockDocUpdaterServer.running = true;
|
||||
return callback(error);
|
||||
}).on("error", function(error) {
|
||||
console.error("error starting MockDocUpdaterServer:", error.message);
|
||||
return process.exit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
sinon.spy MockDocUpdaterServer, "getDocument"
|
||||
sinon.spy(MockDocUpdaterServer, "getDocument");
|
||||
|
||||
@@ -1,46 +1,64 @@
|
||||
sinon = require "sinon"
|
||||
express = require "express"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let MockWebServer;
|
||||
const sinon = require("sinon");
|
||||
const express = require("express");
|
||||
|
||||
module.exports = MockWebServer =
|
||||
projects: {}
|
||||
privileges: {}
|
||||
module.exports = (MockWebServer = {
|
||||
projects: {},
|
||||
privileges: {},
|
||||
|
||||
createMockProject: (project_id, privileges, project) ->
|
||||
MockWebServer.privileges[project_id] = privileges
|
||||
MockWebServer.projects[project_id] = project
|
||||
createMockProject(project_id, privileges, project) {
|
||||
MockWebServer.privileges[project_id] = privileges;
|
||||
return MockWebServer.projects[project_id] = project;
|
||||
},
|
||||
|
||||
joinProject: (project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
|
||||
callback(
|
||||
joinProject(project_id, user_id, callback) {
|
||||
if (callback == null) { callback = function(error, project, privilegeLevel) {}; }
|
||||
return callback(
|
||||
null,
|
||||
MockWebServer.projects[project_id],
|
||||
MockWebServer.privileges[project_id][user_id]
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
joinProjectRequest: (req, res, next) ->
|
||||
{project_id} = req.params
|
||||
{user_id} = req.query
|
||||
if project_id == 'rate-limited'
|
||||
res.status(429).send()
|
||||
else
|
||||
MockWebServer.joinProject project_id, user_id, (error, project, privilegeLevel) ->
|
||||
return next(error) if error?
|
||||
res.json {
|
||||
project: project
|
||||
privilegeLevel: privilegeLevel
|
||||
}
|
||||
joinProjectRequest(req, res, next) {
|
||||
const {project_id} = req.params;
|
||||
const {user_id} = req.query;
|
||||
if (project_id === 'rate-limited') {
|
||||
return res.status(429).send();
|
||||
} else {
|
||||
return MockWebServer.joinProject(project_id, user_id, function(error, project, privilegeLevel) {
|
||||
if (error != null) { return next(error); }
|
||||
return res.json({
|
||||
project,
|
||||
privilegeLevel
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
running: false
|
||||
run: (callback = (error) ->) ->
|
||||
if MockWebServer.running
|
||||
return callback()
|
||||
app = express()
|
||||
app.post "/project/:project_id/join", MockWebServer.joinProjectRequest
|
||||
app.listen 3000, (error) ->
|
||||
MockWebServer.running = true
|
||||
callback(error)
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockWebServer:", error.message
|
||||
process.exit(1)
|
||||
running: false,
|
||||
run(callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
if (MockWebServer.running) {
|
||||
return callback();
|
||||
}
|
||||
const app = express();
|
||||
app.post("/project/:project_id/join", MockWebServer.joinProjectRequest);
|
||||
return app.listen(3000, function(error) {
|
||||
MockWebServer.running = true;
|
||||
return callback(error);
|
||||
}).on("error", function(error) {
|
||||
console.error("error starting MockWebServer:", error.message);
|
||||
return process.exit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
sinon.spy MockWebServer, "joinProject"
|
||||
sinon.spy(MockWebServer, "joinProject");
|
||||
|
||||
@@ -1,75 +1,94 @@
|
||||
XMLHttpRequest = require("../../libs/XMLHttpRequest").XMLHttpRequest
|
||||
io = require("socket.io-client")
|
||||
async = require("async")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let Client;
|
||||
const {
|
||||
XMLHttpRequest
|
||||
} = require("../../libs/XMLHttpRequest");
|
||||
const io = require("socket.io-client");
|
||||
const async = require("async");
|
||||
|
||||
request = require "request"
|
||||
Settings = require "settings-sharelatex"
|
||||
redis = require "redis-sharelatex"
|
||||
rclient = redis.createClient(Settings.redis.websessions)
|
||||
const request = require("request");
|
||||
const Settings = require("settings-sharelatex");
|
||||
const redis = require("redis-sharelatex");
|
||||
const rclient = redis.createClient(Settings.redis.websessions);
|
||||
|
||||
uid = require('uid-safe').sync
|
||||
signature = require("cookie-signature")
|
||||
const uid = require('uid-safe').sync;
|
||||
const signature = require("cookie-signature");
|
||||
|
||||
io.util.request = () ->
|
||||
xhr = new XMLHttpRequest()
|
||||
_open = xhr.open
|
||||
xhr.open = () =>
|
||||
_open.apply(xhr, arguments)
|
||||
if Client.cookie?
|
||||
xhr.setRequestHeader("Cookie", Client.cookie)
|
||||
return xhr
|
||||
io.util.request = function() {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const _open = xhr.open;
|
||||
xhr.open = function() {
|
||||
_open.apply(xhr, arguments);
|
||||
if (Client.cookie != null) {
|
||||
return xhr.setRequestHeader("Cookie", Client.cookie);
|
||||
}
|
||||
}.bind(this);
|
||||
return xhr;
|
||||
};
|
||||
|
||||
module.exports = Client =
|
||||
cookie: null
|
||||
module.exports = (Client = {
|
||||
cookie: null,
|
||||
|
||||
setSession: (session, callback = (error) ->) ->
|
||||
sessionId = uid(24)
|
||||
session.cookie = {}
|
||||
rclient.set "sess:" + sessionId, JSON.stringify(session), (error) ->
|
||||
return callback(error) if error?
|
||||
secret = Settings.security.sessionSecret
|
||||
cookieKey = 's:' + signature.sign(sessionId, secret)
|
||||
Client.cookie = "#{Settings.cookieName}=#{cookieKey}"
|
||||
callback()
|
||||
setSession(session, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
const sessionId = uid(24);
|
||||
session.cookie = {};
|
||||
return rclient.set("sess:" + sessionId, JSON.stringify(session), function(error) {
|
||||
if (error != null) { return callback(error); }
|
||||
const secret = Settings.security.sessionSecret;
|
||||
const cookieKey = 's:' + signature.sign(sessionId, secret);
|
||||
Client.cookie = `${Settings.cookieName}=${cookieKey}`;
|
||||
return callback();
|
||||
});
|
||||
},
|
||||
|
||||
unsetSession: (callback = (error) ->) ->
|
||||
Client.cookie = null
|
||||
callback()
|
||||
unsetSession(callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
Client.cookie = null;
|
||||
return callback();
|
||||
},
|
||||
|
||||
connect: (cookie) ->
|
||||
client = io.connect("http://localhost:3026", 'force new connection': true)
|
||||
client.on 'connectionAccepted', (_, publicId) ->
|
||||
client.publicId = publicId
|
||||
return client
|
||||
connect(cookie) {
|
||||
const client = io.connect("http://localhost:3026", {'force new connection': true});
|
||||
client.on('connectionAccepted', (_, publicId) => client.publicId = publicId);
|
||||
return client;
|
||||
},
|
||||
|
||||
getConnectedClients: (callback = (error, clients) ->) ->
|
||||
request.get {
|
||||
url: "http://localhost:3026/clients"
|
||||
getConnectedClients(callback) {
|
||||
if (callback == null) { callback = function(error, clients) {}; }
|
||||
return request.get({
|
||||
url: "http://localhost:3026/clients",
|
||||
json: true
|
||||
}, (error, response, data) ->
|
||||
callback error, data
|
||||
}, (error, response, data) => callback(error, data));
|
||||
},
|
||||
|
||||
getConnectedClient: (client_id, callback = (error, clients) ->) ->
|
||||
request.get {
|
||||
url: "http://localhost:3026/clients/#{client_id}"
|
||||
getConnectedClient(client_id, callback) {
|
||||
if (callback == null) { callback = function(error, clients) {}; }
|
||||
return request.get({
|
||||
url: `http://localhost:3026/clients/${client_id}`,
|
||||
json: true
|
||||
}, (error, response, data) ->
|
||||
callback error, data
|
||||
}, (error, response, data) => callback(error, data));
|
||||
},
|
||||
|
||||
|
||||
disconnectClient: (client_id, callback) ->
|
||||
request.post {
|
||||
url: "http://localhost:3026/client/#{client_id}/disconnect"
|
||||
disconnectClient(client_id, callback) {
|
||||
request.post({
|
||||
url: `http://localhost:3026/client/${client_id}/disconnect`,
|
||||
auth: {
|
||||
user: Settings.internal.realTime.user,
|
||||
pass: Settings.internal.realTime.pass
|
||||
}
|
||||
}, (error, response, data) ->
|
||||
callback error, data
|
||||
return null
|
||||
}, (error, response, data) => callback(error, data));
|
||||
return null;
|
||||
},
|
||||
|
||||
disconnectAllClients: (callback) ->
|
||||
Client.getConnectedClients (error, clients) ->
|
||||
async.each clients, (clientView, cb) ->
|
||||
Client.disconnectClient clientView.client_id, cb
|
||||
, callback
|
||||
disconnectAllClients(callback) {
|
||||
return Client.getConnectedClients((error, clients) => async.each(clients, (clientView, cb) => Client.disconnectClient(clientView.client_id, cb)
|
||||
, callback));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
app = require('../../../../app')
|
||||
logger = require("logger-sharelatex")
|
||||
Settings = require("settings-sharelatex")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS103: Rewrite code to no longer use __guard__
|
||||
* DS205: Consider reworking code to avoid use of IIFEs
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const app = require('../../../../app');
|
||||
const logger = require("logger-sharelatex");
|
||||
const Settings = require("settings-sharelatex");
|
||||
|
||||
module.exports =
|
||||
running: false
|
||||
initing: false
|
||||
callbacks: []
|
||||
ensureRunning: (callback = (error) ->) ->
|
||||
if @running
|
||||
return callback()
|
||||
else if @initing
|
||||
@callbacks.push callback
|
||||
else
|
||||
@initing = true
|
||||
@callbacks.push callback
|
||||
app.listen Settings.internal?.realtime?.port, "localhost", (error) =>
|
||||
throw error if error?
|
||||
@running = true
|
||||
logger.log("clsi running in dev mode")
|
||||
module.exports = {
|
||||
running: false,
|
||||
initing: false,
|
||||
callbacks: [],
|
||||
ensureRunning(callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
if (this.running) {
|
||||
return callback();
|
||||
} else if (this.initing) {
|
||||
return this.callbacks.push(callback);
|
||||
} else {
|
||||
this.initing = true;
|
||||
this.callbacks.push(callback);
|
||||
return app.listen(__guard__(Settings.internal != null ? Settings.internal.realtime : undefined, x => x.port), "localhost", error => {
|
||||
if (error != null) { throw error; }
|
||||
this.running = true;
|
||||
logger.log("clsi running in dev mode");
|
||||
|
||||
for callback in @callbacks
|
||||
callback()
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (callback of Array.from(this.callbacks)) {
|
||||
result.push(callback());
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function __guard__(value, transform) {
|
||||
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user