Просмотр исходного кода

Clean up dead code and add acceptance tests

James Allen 9 лет назад
Родитель
Сommit
6ac2aa77a8
22 измененных файлов с 181 добавлено и 972 удалено
  1. 1 0
      services/chat/.gitignore
  2. 17 3
      services/chat/Gruntfile.coffee
  3. 0 23
      services/chat/app/coffee/Features/Authentication/AuthenticationController.coffee
  4. 0 24
      services/chat/app/coffee/Features/Authorization/AuthorizationManager.coffee
  5. 0 84
      services/chat/app/coffee/Features/Messages/MessageController.coffee
  6. 0 101
      services/chat/app/coffee/Features/Rooms/RoomController.coffee
  7. 0 25
      services/chat/app/coffee/Features/Sockets/RealTimeEventManager.coffee
  8. 0 40
      services/chat/app/coffee/Features/Sockets/SocketManager.coffee
  9. 1 0
      services/chat/app/coffee/Features/Users/UserFormatter.coffee
  10. 18 12
      services/chat/app/coffee/Features/WebApi/WebApiManager.coffee
  11. 1 20
      services/chat/app/coffee/router.coffee
  12. 1 8
      services/chat/app/coffee/server.coffee
  13. 3 4
      services/chat/package.json
  14. 73 0
      services/chat/test/acceptance/coffee/GettingMessagesTests.coffee
  15. 23 0
      services/chat/test/acceptance/coffee/SendingAMessageTests.coffee
  16. 16 0
      services/chat/test/acceptance/coffee/helpers/ChatClient.coffee
  17. 27 0
      services/chat/test/acceptance/coffee/helpers/MockWebApi.coffee
  18. 0 71
      services/chat/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee
  19. 0 55
      services/chat/test/unit/coffee/Authorization/AuthorizationManagerTests.coffee
  20. 0 187
      services/chat/test/unit/coffee/Messages/MessageControllerTests.coffee
  21. 0 240
      services/chat/test/unit/coffee/Rooms/RoomControllerTests.coffee
  22. 0 75
      services/chat/test/unit/coffee/Sockets/RealTimeEventManagerTests.coffee

+ 1 - 0
services/chat/.gitignore

@@ -3,6 +3,7 @@
 app.js
 app/js/
 test/unit/js/
+test/acceptance/js/
 public/build/
 
 node_modules/

+ 17 - 3
services/chat/Gruntfile.coffee

@@ -30,7 +30,7 @@ module.exports = (grunt) ->
 				dest: './',
 				ext: '.js'
 
-			server_tests:
+			unit_tests:
 				expand: true,
 				flatten: false,
 				cwd: 'test/unit/coffee',
@@ -38,10 +38,18 @@ module.exports = (grunt) ->
 				dest: 'test/unit/js/',
 				ext: '.js'
 
+			acceptance_tests:
+				expand: true,
+				flatten: false,
+				cwd: 'test/acceptance/coffee',
+				src: ['**/*.coffee'],
+				dest: 'test/acceptance/js/',
+				ext: '.js'
+
 		watch:
 			server_coffee:
 				files: ['app/**/*.coffee', 'test/unit/**/*.coffee']
-				tasks: ['compile:server', 'compile:server_tests', 'mochaTest']
+				tasks: ['compile:server', 'compile:unit_tests', 'mochaTest']
 
 			client_coffee:
 				files: ['public/**/*.coffee']
@@ -101,7 +109,12 @@ module.exports = (grunt) ->
 				options:
 					reporter: process.env.MOCHA_RUNNER || "spec"
 					grep: grunt.option("grep")
-				src: ['test/**/*.js']
+				src: ['test/unit/**/*.js']
+			acceptance:
+				options:
+					reporter: process.env.MOCHA_RUNNER || "spec"
+					grep: grunt.option("grep")
+				src: ['test/acceptance/**/*.js']
 
 		plato:
 			your_task:
@@ -128,4 +141,5 @@ module.exports = (grunt) ->
 	grunt.registerTask 'default', ['compile', 'bunyan', 'execute']
 	grunt.registerTask 'compileAndCompress', ['compile', 'uglify']
 	grunt.registerTask 'test:unit', ['compile', 'mochaTest:unit']
+	grunt.registerTask 'test:acceptance', ['compile:acceptance_tests', 'mochaTest:acceptance']
 

+ 0 - 23
services/chat/app/coffee/Features/Authentication/AuthenticationController.coffee

@@ -1,23 +0,0 @@
-async = require "async"
-logger = require "logger-sharelatex"
-WebApiManager = require("../WebApi/WebApiManager")
-UserFormatter = require("../Users/UserFormatter")
-
-module.exports = AuthenticationController =
-	authClient: (client, data, callback = (error) ->) ->
-		logger.log auth_token: data.auth_token, "authenticating user"
-		WebApiManager.getUserDetailsFromAuthToken data.auth_token, (error, user) =>
-			if error?
-				logger.error data: data, client_id: client.id, err: error, "error authenticating user"
-				return callback("something went wrong")
-			logger.log user: user, auth_token: data.auth_token, "authenticated user"
-			user = UserFormatter.formatUserForClientSide user
-			jobs = []
-			for key, value of user
-				do (key, value) ->
-					jobs.push (callback) -> client.set key, value, callback
-			jobs.push (callback) -> client.set "auth_token", data.auth_token, callback
-			async.series jobs, (error, results) =>
-				callback(error, user)
-			
-			

+ 0 - 24
services/chat/app/coffee/Features/Authorization/AuthorizationManager.coffee

@@ -1,24 +0,0 @@
-WebApiManager = require "../WebApi/WebApiManager"
-SocketManager = require "../Sockets/SocketManager"
-
-module.exports = AuthorizationManager =
-	canClientJoinProjectRoom: (client, project_id, callback = (error, authorized) ->) ->
-		client.get "auth_token", (error, auth_token) ->
-			return callback(error) if error?
-			WebApiManager.getProjectCollaborators project_id, auth_token, (error, collaborators) ->
-				return callback(error) if error?
-				client.get "id", (error, user_id) ->
-					return callback(error) if error?
-					authorized = false
-					for collaborator in collaborators
-						if collaborator.id == user_id
-							authorized = true
-							break
-					callback null, authorized
-
-	canClientSendMessageToRoom: (client, room_id, callback = (error, authorized) ->) ->
-		SocketManager.isClientInRoom(client, room_id, callback)
-
-	canClientReadMessagesInRoom: (client, room_id, callback = (error, authorized) ->) ->
-		SocketManager.isClientInRoom(client, room_id, callback)
-				

+ 0 - 84
services/chat/app/coffee/Features/Messages/MessageController.coffee

@@ -1,84 +0,0 @@
-logger = require "logger-sharelatex"
-metrics = require "metrics-sharelatex"
-MessageManager = require "./MessageManager"
-MessageFormatter = require "./MessageFormatter"
-SocketManager = require "../Sockets/SocketManager"
-AuthorizationManager = require "../Authorization/AuthorizationManager"
-
-
-module.exports = MessageController =
-	DEFAULT_MESSAGE_LIMIT: 50
-
-	sendMessage: (client, data, callback = (error) ->) ->
-		content = data?.message?.content
-		room_id = data?.room?.id
-		return callback("malformed message") if not (content? and room_id?)
-
-		client.get "id", (error, user_id) ->
-			logger.log user_id: user_id, room_id: room_id, "sending message"
-			AuthorizationManager.canClientSendMessageToRoom client, room_id, (error, authorized) ->
-				if error?
-					logger.err err:error, user_id:user_id, "something went wrong checking if canClientSendMessageToRoom"
-					return callback("something went wrong") 
-				if authorized
-					SocketManager.getClientAttributes client, ["id"], (error, values) ->
-						if error?
-							logger.err err:error, user_id:user_id, "something went wrong getClientAttributes"
-							return callback("something went wrong") 
-						newMessageOpts = 
-							content: content
-							room_id: room_id
-							user_id: values[0]
-							timestamp: Date.now()
-						MessageManager.createMessage newMessageOpts, (error, message) ->
-							if error?
-								logger.err err:error, user_id:user_id, "something went wrong createMessage"
-								return callback("something went wrong") 
-							MessageManager.populateMessagesWithUsers [message], (error, messages) ->
-								if error?
-									logger.err err:error, user_id:user_id, "something went wrong populateMessagesWithUsers"
-									return callback("something went wrong") 
-								message = MessageFormatter.formatMessageForClientSide(messages[0])
-								message.room =
-									id: room_id
-								SocketManager.emitToRoom data.room.id, "messageReceived", message:message
-								metrics.inc "editor.instant-message"
-								logger.log user_id: user_id, room_id: room_id, "sent message"
-								callback()
-				else
-					logger.log user_id: user_id, room_id: room_id, "unauthorized attempt to send message"
-					callback("unknown room")
-
-	getMessages: (client, data, callback = (error, messages) ->) ->
-		room_id = data?.room?.id
-		return callback("malformed message") if not room_id?
-
-		client.get "id", (error, user_id) ->
-			logger.log user_id: user_id, room_id: room_id, "getting messages"
-			AuthorizationManager.canClientReadMessagesInRoom client, room_id, (error, authorized) ->
-				if error?
-					logger.err err:error, user_id:user_id, "something went canClientReadMessagesInRoom"
-					return callback("something went wrong") 
-				if authorized
-					query = room_id: room_id
-					if data.before?
-						query.timestamp = $lt: data.before
-					options =
-						order_by: "timestamp"
-						sort_order: -1
-						limit: data.limit || MessageController.DEFAULT_MESSAGE_LIMIT
-					MessageManager.getMessages query, options, (error, messages) ->
-						if error?
-							logger.err err:error, user_id:user_id, "something went getMessages"
-							return callback("something went wrong") 
-						MessageManager.populateMessagesWithUsers messages, (error, messages) ->
-							if error?
-								logger.err err:error, user_id:user_id, "something went populateMessagesWithUsers"
-								return callback("something went wrong") 
-							messages = MessageFormatter.formatMessagesForClientSide messages
-							logger.log user_id: user_id, room_id: room_id, "got messages"
-							callback null, messages
-				else
-					logger.log user_id: user_id, room_id: room_id, "unauthorized attempt to get messages"
-					callback("unknown room")
-

+ 0 - 101
services/chat/app/coffee/Features/Rooms/RoomController.coffee

@@ -1,101 +0,0 @@
-async = require "async"
-logger = require "logger-sharelatex"
-AuthorizationManager = require "../Authorization/AuthorizationManager"
-RoomManager = require "../Rooms/RoomManager"
-SocketManager = require "../Sockets/SocketManager"
-
-module.exports = RoomController =
-	joinRoom: (client, data, callback = (error) ->) ->
-		if !data.room?.project_id?
-			return callback("unknown room")
-		project_id = data.room.project_id
-
-		client.get "id", (error, id) ->
-			logger.log user_id: id, project_id: project_id, "joining room"
-			AuthorizationManager.canClientJoinProjectRoom client, project_id, (error, authorized) ->
-				return callback("something went wrong") if error?
-				if authorized
-					RoomManager.findOrCreateRoom project_id: project_id, (error, room) ->
-						return callback("something went wrong") if error?
-						room_id = room._id.toString()
-						RoomController._addClientToRoom client, room_id, (error) ->
-							return callback("something went wrong") if error?
-							RoomController._getClientsInRoom room_id, (error, clients) ->
-								return callback("something went wrong") if error?
-								logger.log user_id: id, project_id: project_id, room_id: room_id, "joined room"
-								roomDetails =
-									room:
-										id: room_id
-										connectedUsers: clients
-								callback null, roomDetails
-				else
-					logger.log user_id: id, project_id: project_id, "unauthorized attempt to join room"
-					callback("unknown room")
-
-	leaveAllRooms: (client, callback = (error) ->) ->
-		client.get "id", (error, id) ->
-			logger.log user_id: id, "leaving all rooms"
-			SocketManager.getRoomIdsClientHasJoined client, (error, room_ids) ->
-				return callback("something went wrong") if error?
-				jobs = []
-				for room_id in room_ids
-					do (room_id) ->
-						jobs.push (callback) ->
-							RoomController.leaveRoom client, room_id, callback
-				async.series jobs, (error)-> callback(error)
-		
-	leaveRoom: (client, room_id, callback = (error) ->) ->
-		client.get "id", (error, id) ->
-			logger.log user_id: id, room_id: room_id, "leaving room"
-			RoomController._getClientAttributes client, (error, attributes) ->
-				return callback("something went wrong") if error?
-				SocketManager.removeClientFromRoom client, room_id, (error) ->
-					return callback("something went wrong") if error?
-					leftRoomUpdate =
-						room:
-							id: room_id
-						user: attributes
-					SocketManager.emitToRoom room_id, "userLeft", leftRoomUpdate
-					logger.log user_id: id, room_id: room_id, "left room"
-					callback()
-
-	_addClientToRoom: (client, room_id, callback = (error) ->) ->
-		RoomController._getClientAttributes client, (error, attributes) ->
-			return callback(error) if error?
-			update =
-				room:
-					id: room_id
-				user: attributes
-			SocketManager.emitToRoom room_id, "userJoined", update
-			SocketManager.addClientToRoom client, room_id, callback
-
-	_getClientsInRoom: (room_id, callback = (error, clients) ->) ->
-		SocketManager.getClientsInRoom room_id, (error, clients) ->
-			return callback(error) if error?
-			formattedClients = []
-			jobs = []
-
-			for client in clients
-				do (client) ->
-					jobs.push (callback) ->
-						RoomController._getClientAttributes client, (error, attributes) ->
-							return callback(error) if error?
-							formattedClients.push attributes
-							callback()
-
-			async.series jobs, (error) ->
-				return callback(error) if error?
-				callback null, formattedClients
-	
-	_getClientAttributes: (client, callback = (error, attributes) ->) ->
-		SocketManager.getClientAttributes client, ["id", "first_name", "last_name", "email", "gravatar_url"], (error, attributes) ->
-			return callback(error) if error?
-			[id, first_name, last_name, email, gravatar_url] = attributes
-			clientAttributes =
-				id           : id
-				first_name   : first_name
-				last_name    : last_name
-				email        : email
-				gravatar_url : gravatar_url
-			callback null, clientAttributes
-		

+ 0 - 25
services/chat/app/coffee/Features/Sockets/RealTimeEventManager.coffee

@@ -1,25 +0,0 @@
-settings = require 'settings-sharelatex'
-rclientPub = require("redis").createClient(settings.redis.web.port, settings.redis.web.host)
-rclientPub.auth(settings.redis.web.password)
-rclientSub = require("redis").createClient(settings.redis.web.port, settings.redis.web.host)
-rclientSub.auth(settings.redis.web.password)
-
-module.exports = RealTimeEventManager =
-
-	rclientPub:rclientPub
-	rclientSub:rclientSub
-
-	emitToRoom: (room_id, message, payload...) ->
-		RealTimeEventManager.rclientPub.publish "chat-events", JSON.stringify
-			room_id: room_id
-			message: message
-			payload: payload
-
-	listenForChatEvents: () ->
-		@rclientSub.subscribe "chat-events"
-		@rclientSub.on "message", @_processEditorEvent.bind(@)
-
-	_processEditorEvent: (channel, message) ->
-		io = require('../../server').io
-		message = JSON.parse(message)
-		io.sockets.in(message.room_id).emit(message.message, message.payload...)

+ 0 - 40
services/chat/app/coffee/Features/Sockets/SocketManager.coffee

@@ -1,40 +0,0 @@
-async = require "async"
-RealTimeEventManager = require("./RealTimeEventManager")
-
-module.exports = SocketManager =
-	addClientToRoom: (client, room_id, callback = (error) ->) ->
-		client.join(room_id)
-		callback()
-
-	removeClientFromRoom: (client, room_id, callback = (error) ->) ->
-		client.leave(room_id)
-		callback()
-	
-	getClientAttributes: (client, attributes, callback = (error, values) ->) ->
-		jobs = []
-		for attribute in attributes
-			do (attribute) ->
-				jobs.push (cb) -> client.get attribute, cb
-		async.series jobs, callback
-
-	emitToRoom: RealTimeEventManager.emitToRoom
-
-	isClientInRoom: (targetClient, room_id, callback = (error, inRoom) ->) ->
-		io = require("../../server").io
-		for client in io.sockets.clients(room_id)
-			if client.id == targetClient.id
-				return callback null, true
-		callback null, false
-
-	getClientsInRoom: (room_id, callback = (error, clients) ->) ->
-		io = require("../../server").io
-		callback null, io.sockets.clients(room_id)
-
-	getRoomIdsClientHasJoined: (client, callback = (error, room_ids) ->) ->
-		io = require("../../server").io
-		room_ids = []
-		for room_id, value of io.sockets.manager.roomClients[client.id]
-			if room_id[0] == "/"
-				room_ids.push room_id.slice(1)
-		callback null, room_ids
-		

+ 1 - 0
services/chat/app/coffee/Features/Users/UserFormatter.coffee

@@ -2,6 +2,7 @@ crypto = require "crypto"
 
 module.exports = UserFormatter =
 	formatUserForClientSide: (user) ->
+		return null if !user?
 		if user._id?
 			user.id = user._id.toString()
 			delete user._id

+ 18 - 12
services/chat/app/coffee/Features/WebApi/WebApiManager.coffee

@@ -16,14 +16,16 @@ module.exports = WebApiManager =
 		options.method = method
 		request options, (error, response, body) ->
 			return callback(error) if error?
-			try
-				result = JSON.parse(body)
-			catch e
-				return callback(e)
-			return callback null, result
-
-	getUserDetailsFromAuthToken: (auth_token, callback = (error, details) ->) ->
-		@apiRequest "/user/personal_info?auth_token=#{auth_token}", "get", callback
+			if 200 <= response.statusCode < 300
+				try
+					result = JSON.parse(body)
+				catch e
+					return callback(e)
+				return callback null, result
+			else
+				error = new Error("web api returned non-success code: #{response.statusCode}")
+				error.statusCode = response.statusCode
+				return callback error
 
 	getUserDetails: (user_id, callback = (error, details) ->) ->
 		@apiRequest "/user/#{user_id}/personal_info", "get", {
@@ -31,7 +33,11 @@ module.exports = WebApiManager =
 				user: Settings.apis.web.user
 				pass: Settings.apis.web.pass
 				sendImmediately: true
-		}, callback
-
-	getProjectCollaborators: (project_id, auth_token, callback = (error, collaborators) ->) ->
-		@apiRequest "/project/#{project_id}/collaborators?auth_token=#{auth_token}", "get", callback
+		}, (error, data) ->
+			if error?
+				if error.statusCode == 404
+					return callback null, null
+				else
+					return callback error
+			else
+				return callback null, data

+ 1 - 20
services/chat/app/coffee/router.coffee

@@ -1,31 +1,12 @@
-AuthenticationController = require("./Features/Authentication/AuthenticationController")
-MessageController = require("./Features/Messages/MessageController")
-RoomController = require("./Features/Rooms/RoomController")
 MessageHttpController = require('./Features/Messages/MessageHttpController')
 
 module.exports = Router =
-	route: (app, io) ->
-
+	route: (app) ->
 		app.get "/room/:project_id/messages", MessageHttpController.getMessages
 		app.post "/room/:project_id/messages", MessageHttpController.sendMessage
 		
 		app.get "/status", (req, res, next) ->
 			res.send("chat is alive")
 
-		io.sockets.on "connection", (client) ->
-			client.on "disconnect", () ->
-				RoomController.leaveAllRooms(client)
-
-			client.on "auth", (data, callback = (error) ->) ->
-				AuthenticationController.authClient(client, data, callback)
-
-			client.on "joinRoom", (data, callback = (error) ->) ->
-				RoomController.joinRoom(client, data, callback)
-
-			client.on "sendMessage", (data, callback = (error) ->) ->
-				MessageController.sendMessage(client, data, callback)
-
-			client.on "getMessages", (data, callback = (error) ->) ->
-				MessageController.getMessages(client, data, callback)
 
 			

+ 1 - 8
services/chat/app/coffee/server.coffee

@@ -6,16 +6,13 @@ Path = require("path")
 express = require("express")
 app = express()
 server = require("http").createServer(app)
-io = require("socket.io").listen(server)
-io.set("resource", "/chat/socket.io")
-io.set("log level", 1)
 Router = require "./router"
 
 metrics.mongodb.monitor(Path.resolve(__dirname + "/../../node_modules/mongojs/node_modules/mongodb"), logger)
 
 app.use express.bodyParser()
 app.use metrics.http.monitor(logger)
-Router.route(app, io)
+Router.route(app)
 
 if (app.get 'env') == 'development'
 	console.log "Development Enviroment"
@@ -39,13 +36,9 @@ app.use (req, res, next) ->
 
 app.use(express.static(__dirname + "/../../public/build"))
 
-
-
 module.exports = {
 	server: server
-	io: io
 	app: app
 }
 
-require("./Features/Sockets/RealTimeEventManager").listenForChatEvents()
 

+ 3 - 4
services/chat/package.json

@@ -8,15 +8,14 @@
   },
   "dependencies": {
     "async": "0.2.9",
+    "coffee-script": "~1.7.1",
     "express": "3.3.1",
-    "request": "2.21.0",
-    "socket.io": "0.9.14",
-    "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
     "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
     "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0",
     "mongojs": "0.18.2",
     "redis": "~0.10.1",
-    "coffee-script": "~1.7.1"
+    "request": "^2.79.0",
+    "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0"
   },
   "devDependencies": {
     "bunyan": "^1.0.0",

+ 73 - 0
services/chat/test/acceptance/coffee/GettingMessagesTests.coffee

@@ -0,0 +1,73 @@
+{ObjectId} = require "../../../app/js/mongojs"
+expect = require("chai").expect
+async = require "async"
+crypto = require "crypto"
+
+MockWebApi = require "./helpers/MockWebApi"
+ChatClient = require "./helpers/ChatClient"
+
+describe "Getting messages", ->
+	before ->
+		@user_id1 = ObjectId().toString()
+		@user_id2 = ObjectId().toString()
+		@content1 = "foo bar"
+		@content2 = "hello world"
+		MockWebApi.addUser @user_id1, @user1 = {
+			id: @user_id1
+			first_name: "Jane"
+			last_name: "Smith"
+			email: "jane@example.com"
+		}
+		MockWebApi.addUser @user_id2, @user2 = {
+			id: @user_id2
+			first_name: "John"
+			last_name: "Doe"
+			email: "john@example.com"
+		}
+
+	describe "normally", ->
+		before (done) ->
+			@project_id = ObjectId().toString()
+			async.series [
+				(cb) => ChatClient.sendMessage @project_id, @user_id1, @content1, cb
+				(cb) => ChatClient.sendMessage @project_id, @user_id2, @content2, cb
+			], done
+		
+		it "should contain the messages and populated users when getting the messages", (done) ->
+			ChatClient.getMessages @project_id, (error, response, messages) =>
+				expect(messages.length).to.equal 2
+				messages.reverse()
+				expect(messages[0].content).to.equal @content1
+				expect(messages[0].user).to.deep.equal {
+					id: @user_id1
+					first_name: "Jane"
+					last_name: "Smith"
+					email: "jane@example.com"
+					gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("jane@example.com").digest("hex")}"
+				}
+				expect(messages[1].content).to.equal @content2
+				expect(messages[1].user).to.deep.equal {
+					id: @user_id2
+					first_name: "John"
+					last_name: "Doe"
+					email: "john@example.com"
+					gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("john@example.com").digest("hex")}"
+				}
+				done()
+
+	describe "when a user doesn't exit", ->
+		before (done) ->
+			@project_id = ObjectId().toString()
+			@user_id3 = ObjectId().toString()
+			async.series [
+				(cb) => ChatClient.sendMessage @project_id, @user_id3, @content1, cb
+				(cb) => ChatClient.sendMessage @project_id, @user_id2, @content2, cb
+			], done
+		
+		it "should just return null for the user", (done) ->
+			ChatClient.getMessages @project_id, (error, response, messages) =>
+				expect(messages.length).to.equal 2
+				messages.reverse()
+				expect(messages[0].content).to.equal @content1
+				expect(messages[0].user).to.equal null
+				done()

+ 23 - 0
services/chat/test/acceptance/coffee/SendingAMessageTests.coffee

@@ -0,0 +1,23 @@
+{ObjectId} = require "../../../app/js/mongojs"
+expect = require("chai").expect
+
+MockWebApi = require "./helpers/MockWebApi"
+ChatClient = require "./helpers/ChatClient"
+
+describe "Sending a message", ->
+	before (done) ->
+		@project_id = ObjectId().toString()
+		@user_id = ObjectId().toString()
+		@content = "foo bar"
+		ChatClient.sendMessage @project_id, @user_id, @content, (error, response, body) ->
+			expect(error).to.be.null
+			expect(response.statusCode).to.equal 201
+			done()
+	
+	it "should then list the message in project messages", (done) ->
+		ChatClient.getMessages @project_id, (error, response, messages) =>
+			expect(error).to.be.null
+			expect(response.statusCode).to.equal 200
+			expect(messages.length).to.equal 1
+			expect(messages[0].content).to.equal @content
+			done()

+ 16 - 0
services/chat/test/acceptance/coffee/helpers/ChatClient.coffee

@@ -0,0 +1,16 @@
+request = require("request").defaults({baseUrl: "http://localhost:3010"})
+
+module.exports =
+	sendMessage: (project_id, user_id, content, callback) ->
+		request.post {
+			url: "/room/#{project_id}/messages"
+			json:
+				user_id: user_id
+				content: content
+		}, callback
+	
+	getMessages: (project_id, callback) ->
+		request.get {
+			url: "/room/#{project_id}/messages",
+			json: true
+		}, callback

+ 27 - 0
services/chat/test/acceptance/coffee/helpers/MockWebApi.coffee

@@ -0,0 +1,27 @@
+express = require("express")
+app = express()
+
+module.exports = MockWebApi =
+	users: {}
+	
+	addUser: (user_id, user) ->
+		@users[user_id] = user
+	
+	getUser: (user_id, callback = (error, user) ->) ->
+		return callback null, @users[user_id]
+
+	run: () ->
+		app.get "/user/:user_id/personal_info", (req, res, next) =>
+			@getUser req.params.user_id, (error, user) ->
+				if error?
+					res.send 500
+				else if user?
+					res.send JSON.stringify user
+				else
+					res.send 404
+
+		app.listen 3000, (error) ->
+			throw error if error?
+
+MockWebApi.run()
+

+ 0 - 71
services/chat/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee

@@ -1,71 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-expect = chai.expect
-modulePath = "../../../../app/js/Features/Authentication/AuthenticationController.js"
-SandboxedModule = require('sandboxed-module')
-
-describe "AuthenticationController", ->
-	beforeEach ->
-		@AuthenticationController = SandboxedModule.require modulePath, requires:
-			"../WebApi/WebApiManager": @WebApiManager = {}
-			"../Users/UserFormatter": @UserFormatter = {}
-			"logger-sharelatex": @logger = { log: sinon.stub() }
-		@callback = sinon.stub()
-
-	describe "authClient", ->
-		beforeEach ->
-			@auth_token = "super-secret-auth-token"
-			@client =
-				params: {}
-				set: (key, value, callback) ->
-					@params[key] = value
-					callback()
-			@user =
-				id: "user-id-123"
-				email: "doug@sharelatex.com"
-				first_name: "Douglas"
-				last_name: "Adams"
-			@WebApiManager.getUserDetailsFromAuthToken = sinon.stub().callsArgWith(1, null, @user)
-			@UserFormatter.formatUserForClientSide = sinon.stub().returns({
-				id: @user.id
-				first_name: @user.first_name
-				last_name: @user.last_name
-				email: @user.email
-				gravatar_url: "//gravatar/url"
-			})
-			@AuthenticationController.authClient(@client, auth_token: @auth_token, @callback)
-
-		it "should get the user's data from the web api", ->
-			@WebApiManager.getUserDetailsFromAuthToken
-				.calledWith(@auth_token)
-				.should.equal true
-
-		it "should set the user's data and auth_token on the client object", ->
-			@client.params.should.deep.equal {
-				id: @user.id
-				first_name: @user.first_name
-				last_name: @user.last_name
-				email: @user.email
-				gravatar_url: "//gravatar/url"
-				auth_token: @auth_token
-			}
-
-		it "should call the callback with the user details (including the gravatar URL, but not the auth_token)", ->
-			@callback
-				.calledWith(null, {
-					id: @user.id
-					email: @user.email
-					first_name: @user.first_name
-					last_name: @user.last_name
-					gravatar_url: "//gravatar/url"
-				}).should.equal true
-
-		it "should log the request", ->
-			@logger.log
-				.calledWith(auth_token: @auth_token, "authenticating user")
-				.should.equal true
-			@logger.log
-				.calledWith(user: @user, auth_token: @auth_token, "authenticated user")
-				.should.equal true
-

+ 0 - 55
services/chat/test/unit/coffee/Authorization/AuthorizationManagerTests.coffee

@@ -1,55 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-expect = chai.expect
-modulePath = "../../../../app/js/Features/Authorization/AuthorizationManager.js"
-SandboxedModule = require('sandboxed-module')
-events = require "events"
-
-describe "AuthorizationManager", ->
-	beforeEach ->
-		@SocketManager = {}
-		@AuthorizationManager = SandboxedModule.require modulePath, requires:
-			"../WebApi/WebApiManager": @WebApiManager = {}
-			"../Sockets/SocketManager": @SocketManager
-		@callback = sinon.stub()
-		@user_id = "user-id-123"
-		@project_id = "project-id-456"
-		@auth_token = "auth-token-789"
-		@client =
-			params: {}
-			get: (key, callback = (error, value) ->) ->
-				callback null, @params[key]
-
-	describe "canClientJoinProjectRoom", ->
-		beforeEach ->
-			@client.params.auth_token = @auth_token
-			@client.params.id = @user_id
-			
-		describe "when the client is a collaborator", ->
-			beforeEach ->
-				@collaborators = [
-					id: @user_id
-				]
-				@WebApiManager.getProjectCollaborators = sinon.stub().callsArgWith(2, null, @collaborators)
-				@AuthorizationManager.canClientJoinProjectRoom(@client, @project_id, @callback)
-
-			it "should get the list of collaborators from the web api", ->
-				@WebApiManager.getProjectCollaborators
-					.calledWith(@project_id, @auth_token)
-					.should.equal true
-
-			it "should return true", ->
-				@callback.calledWith(null, true).should.equal true
-
-		describe "when the client is not a collaborator", ->
-			beforeEach ->
-				@collaborators = [
-					id: "not the user id"
-				]
-				@WebApiManager.getProjectCollaborators = sinon.stub().callsArgWith(2, null, @collaborators)
-				@AuthorizationManager.canClientJoinProjectRoom(@client, @project_id, @callback)
-
-			it "should return false", ->
-				@callback.calledWith(null, false).should.equal true
-

+ 0 - 187
services/chat/test/unit/coffee/Messages/MessageControllerTests.coffee

@@ -1,187 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-expect = chai.expect
-modulePath = "../../../../app/js/Features/Messages/MessageController.js"
-SandboxedModule = require('sandboxed-module')
-events = require "events"
-tk = require "timekeeper"
-ObjectId = require("mongojs").ObjectId
-
-describe "MessageController", ->
-	beforeEach ->
-		tk.freeze(new Date())
-		@MessageController = SandboxedModule.require modulePath, requires:
-			"./MessageManager": @MessageManager = {}
-			"./MessageFormatter": @MessageFormatter = {}
-			"../Sockets/SocketManager": @SocketManager = {}
-			"../Authorization/AuthorizationManager": @AuthorizationManager = {}
-			"logger-sharelatex": @logger = { log: sinon.stub() }
-			"metrics-sharelatex": @metrics = {inc: sinon.stub()}
-		@callback = sinon.stub()
-		@client =
-			params:
-				id: @user_id = "user-id-123"
-				first_name: @first_name = "Douglas"
-				last_name: @last_name = "Adams"
-				email: @email = "doug@sharelatex.com"
-				gravatar_url: @gravatar_url = "//gravatar/url"
-			get: (key, callback = (error, value) ->) -> callback null, @params[key]
-	
-	afterEach ->
-		tk.reset()
-
-	describe "sendMessage", ->
-		beforeEach ->
-			@MessageManager.createMessage = sinon.stub().callsArg(1)
-			@SocketManager.emitToRoom = sinon.stub()
-			@singlePopulatedMessage = {data:"here"}
-			@formattedMessage = {formatted:true}
-			@MessageFormatter.formatMessageForClientSide = sinon.stub().returns(@formattedMessage)
-			@MessageManager.populateMessagesWithUsers = sinon.stub().callsArgWith(1, null, [@singlePopulatedMessage])
-			@SocketManager.getClientAttributes = (client, attributes, callback) ->
-				values = (client.params[key] for key in attributes)
-				callback null, values
-
-		describe "when the client is authorized to send a message to the room", ->
-			beforeEach ->
-				@AuthorizationManager.canClientSendMessageToRoom = sinon.stub().callsArgWith(2, null, true)
-				@MessageController.sendMessage(@client, {
-					message:
-						content: @content = "Hello world"
-					room:
-						id: @room_id = "room-id-123"
-				}, @callback)
-
-			it "should check that the client can send a message to the room", ->
-				@AuthorizationManager.canClientSendMessageToRoom
-					.calledWith(@client, @room_id)
-					.should.equal true
-
-			it "should insert the message into the database", ->
-				@MessageManager.createMessage
-					.calledWith({
-						content: @content
-						user_id: @user_id
-						room_id: @room_id
-						timestamp: Date.now()
-					})
-					.should.equal true
-
-
-			it "should format the message for the client", ->
-				@MessageFormatter.formatMessageForClientSide.calledWith(@singlePopulatedMessage).should.equal true
-
-			it "should send the formatted message out to the other clients in the room", ->
-				@SocketManager.emitToRoom.calledWith(@room_id, "messageReceived", message:@formattedMessage).should.equal true
-
-			it "should record the message as a metric", ->
-				@metrics.inc
-					.calledWith("editor.instant-message")
-					.should.equal true
-
-			it "should call the callback", ->
-				@callback.called.should.equal true
-
-		describe "when the client is not authorized", ->
-			beforeEach ->
-				@AuthorizationManager.canClientSendMessageToRoom = sinon.stub().callsArgWith(2, null, false)
-				@MessageController.sendMessage(@client, {
-					message:
-						content: @content = "Hello world"
-					room:
-						id: @room_id = "room-id-123"
-				}, @callback)
-
-			it "should not insert the message into the database", ->
-				@MessageManager.createMessage.called.should.equal false
-				
-			it "should not send the message out to the other clients in the room", ->
-				@SocketManager.emitToRoom.called.should.equal false
-
-			it "should call the callback with an error that doesn't give anything away", ->
-				@callback.calledWith("unknown room").should.equal true
-
-	describe "getMessage", ->
-		beforeEach ->
-			@room_id = "room-id-123"
-			@timestamp = Date.now()
-			@limit = 42
-
-		describe "when the client is authorized", ->
-			beforeEach ->
-				@messages = "messages without users stub"
-				@messagesWithUsers = "messages with users stub"
-				@formattedMessages = "formatted messages stub"
-				@MessageManager.getMessages = sinon.stub().callsArgWith(2, null, @messages)
-				@MessageManager.populateMessagesWithUsers = sinon.stub().callsArgWith(1, null, @messagesWithUsers)
-				@AuthorizationManager.canClientReadMessagesInRoom = sinon.stub().callsArgWith(2, null, true)
-				@MessageFormatter.formatMessagesForClientSide = sinon.stub().returns @formattedMessages
-
-			describe "with a timestamp and limit", ->
-				beforeEach ->
-					@MessageController.getMessages(@client, {
-						room:
-							id: @room_id,
-						before: @timestamp,
-						limit: @limit
-					}, @callback)
-
-				it "should get the requested messages", ->
-					@MessageManager.getMessages
-						.calledWith({
-							timestamp: $lt: @timestamp
-							room_id: @room_id
-						}, {
-							limit: @limit
-							order_by: "timestamp"
-							sort_order: -1
-						})
-						.should.equal true
-
-				it "should populate the messages with the users", ->
-					@MessageManager.populateMessagesWithUsers
-						.calledWith(@messages)
-						.should.equal true
-
-				it "should return the formatted messages", ->
-					@MessageFormatter.formatMessagesForClientSide
-						.calledWith(@messagesWithUsers)
-						.should.equal true
-
-				it "should call the callback with the formatted messages", ->
-					@callback
-						.calledWith(null, @formattedMessages)
-						.should.equal true
-
-			describe "without a timestamp or limit", ->
-				beforeEach ->
-					@MessageController.getMessages(@client, {
-						room:
-							id: @room_id,
-					}, @callback)
-				
-				it "should get a default number of messages from the beginning", ->
-					@MessageManager.getMessages
-						.calledWith({
-							room_id: @room_id
-						}, {
-							limit: @MessageController.DEFAULT_MESSAGE_LIMIT
-							order_by: "timestamp"
-							sort_order: -1
-						})
-						.should.equal true
-
-		describe "when the client is not authorized", ->
-			beforeEach ->
-				@AuthorizationManager.canClientReadMessagesInRoom = sinon.stub().callsArgWith(2, null, false)
-				@MessageController.getMessages(@client, {
-					room:
-						id: @room_id,
-					before: @timestamp,
-					limit: @limit
-				}, @callback)
-
-			it "should call the callback with an error", ->
-				@callback.calledWith("unknown room").should.equal true
-				

+ 0 - 240
services/chat/test/unit/coffee/Rooms/RoomControllerTests.coffee

@@ -1,240 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-expect = chai.expect
-modulePath = "../../../../app/js/Features/Rooms/RoomController.js"
-SandboxedModule = require('sandboxed-module')
-events = require "events"
-ObjectId = require("mongojs").ObjectId
-
-class MockClient
-	params: {}
-	get: (key, callback = (error, value) ->) ->
-		callback null, @params[key]
-
-describe "RoomController", ->
-	beforeEach ->
-		@SocketManager =
-			getClientAttributes: sinon.stub()
-
-
-		@RoomController = SandboxedModule.require modulePath, requires:
-			"../Authorization/AuthorizationManager": @AuthorizationManager = {}
-			"../Sockets/SocketManager": @SocketManager
-			"../Rooms/RoomManager": @RoomManager = {}
-			"logger-sharelatex": @logger = { log: sinon.stub() }
-
-
-		@project_id = ObjectId().toString()
-		@room_id = ObjectId().toString()
-		@room =
-			_id: ObjectId(@room_id)
-			project_id: ObjectId(@project_id)
-		@callback = sinon.stub()
-		@client =
-			params: {}
-			get: (key, callback = (error, value) ->) -> callback null, @params[key]
-
-	describe "joinRoom", ->
-		describe "when the client is authorized", ->
-			beforeEach ->
-				@AuthorizationManager.canClientJoinProjectRoom = sinon.stub().callsArgWith(2, null, true)
-				@RoomManager.findOrCreateRoom = sinon.stub().callsArgWith(1, null, @room)
-				@RoomController._addClientToRoom = sinon.stub().callsArg(2)
-				@RoomController._getClientsInRoom = sinon.stub().callsArgWith(1, null, @clients = ["client1", "client2"])
-				@RoomController.joinRoom @client, { room: project_id: @project_id }, @callback
-
-			it "should check that the client can join the room", ->
-				@AuthorizationManager.canClientJoinProjectRoom
-					.calledWith(@client, @project_id)
-					.should.equal true
-
-			it "should ensure that the room exists", ->
-				@RoomManager.findOrCreateRoom
-					.calledWith({ project_id: @project_id })
-					.should.equal true
-
-			it "should put the client into the room", ->
-				@RoomController._addClientToRoom
-					.calledWith(@client, @room_id)
-					.should.equal true
-
-			it "should get the clients already in the room", ->
-				@RoomController._getClientsInRoom
-					.calledWith(@room_id)
-					.should.equal true
-
-			it "should call the callback with the room id", ->
-				@callback.calledWith(null, {
-					room:
-						id: @room_id
-						connectedUsers: @clients
-				}).should.equal true
-
-		describe "when the client is not authorized", ->
-			beforeEach ->
-				@AuthorizationManager.canClientJoinProjectRoom = sinon.stub().callsArgWith(2, null, false)
-				@RoomController._addClientToRoom = sinon.stub().callsArg(2)
-				@RoomController.joinRoom @client, { room: project_id: @project_id }, @callback
-
-			it "should not put the client into the room", ->
-				@RoomController._addClientToRoom.called.should.equal false
-
-			it "should call the callback with an error that gives nothing away", ->
-				@callback.calledWith("unknown room").should.equal true
-
-	describe "leaveAllRooms", ->
-
-		beforeEach ->
-			@client = new MockClient()
-			@client.params =
-				id: "client-1-id"
-				first_name: "Douglas"
-				last_name: "Adams"
-				email: "doug@sharelatex.com"
-				gravatar_url: "//gravatar/url/1"
-			@room_ids = ["room-id-1", "room-id-2"]
-			@SocketManager.getRoomIdsClientHasJoined = sinon.stub().callsArgWith(1, null, @room_ids)
-			@RoomController.leaveRoom = sinon.stub().callsArg(2)
-			@RoomController.leaveAllRooms @client, @callback
-
-		it "should get the rooms the client has joined", ->
-			@SocketManager.getRoomIdsClientHasJoined
-				.calledWith(@client)
-				.should.equal true
-
-		it "should leave each room", ->
-			for room_id in @room_ids
-				@RoomController.leaveRoom
-					.calledWith(@client, room_id)
-					.should.equal true
-
-		it "should call the callback", ->
-			@callback.called.should.equal true
-
-	describe "leaveRoom", ->
-		beforeEach ->
-			@client = new MockClient()
-			@client.params =
-				id: "client-1-id"
-				first_name: "Douglas"
-				last_name: "Adams"
-				email: "doug@sharelatex.com"
-				gravatar_url: "//gravatar/url/1"
-			@RoomController._getClientAttributes = sinon.stub().callsArgWith(1, null, @client.params)
-
-			@SocketManager.removeClientFromRoom = sinon.stub().callsArg(2)
-			@SocketManager.emitToRoom = sinon.stub()
-			@RoomController.leaveRoom @client, @room_id, @callback
-
-		it "should leave the room", ->
-			@SocketManager.removeClientFromRoom
-				.calledWith(@client, @room_id)
-				.should.equal true
-
-		it "should tell the other clients in the room that we have left", ->
-			@SocketManager.emitToRoom
-				.calledWith(@room_id, "userLeft", {
-					room:
-						id: @room_id
-					user:
-						id           : @client.params["id"]
-						first_name   : @client.params["first_name"]
-						last_name    : @client.params["last_name"]
-						email        : @client.params["email"]
-						gravatar_url : @client.params["gravatar_url"]
-				})
-				.should.equal true
-
-		it "should call the callback", ->
-			@callback.called.should.equal true
-
-	describe "_getClientsInRoom", ->
-		beforeEach ->
-			@client1 = new MockClient()
-			@client1.params =
-				id: "client-1-id"
-				first_name: "Douglas"
-				last_name: "Adams"
-				email: "doug@sharelatex.com"
-				gravatar_url: "//gravatar/url/1"
-			@client2 = new MockClient()
-			@client2.params =
-				id: "client-2-id"
-				first_name: "James"
-				last_name: "Allen"
-				email: "james@sharelatex.com"
-				gravatar_url: "//gravatar/url/2"
-			@clients = [ @client1, @client2 ]
-			callCount = 0
-			
-			@RoomController._getClientAttributes = (ignore, cb)=>
-				if callCount == 0
-					callCount++
-					cb(null, @client1.params)
-				else
-					cb null, @client2.params
-
-
-			@SocketManager.getClientsInRoom = sinon.stub().callsArgWith(1, null, @clients)
-			@RoomController._getClientsInRoom(@room_id, @callback)
-
-		it "should get the socket.io clients in the room", ->
-			@SocketManager.getClientsInRoom
-				.calledWith(@room_id)
-				.should.equal true
-
-		it "should return a formatted array of clients", ->
-			@callback
-				.calledWith(null, [{
-					id           : @client1.params["id"]
-					first_name   : @client1.params["first_name"]
-					last_name    : @client1.params["last_name"]
-					email        : @client1.params["email"]
-					gravatar_url : @client1.params["gravatar_url"]
-				}, {
-					id           : @client2.params["id"]
-					first_name   : @client2.params["first_name"]
-					last_name    : @client2.params["last_name"]
-					email        : @client2.params["email"]
-					gravatar_url : @client2.params["gravatar_url"]
-				}])
-				.should.equal true
-
-	describe "_addClientToRoom", ->
-		beforeEach ->
-			@client = new MockClient()
-			@client.params =
-				id: "client-1-id"
-				first_name: "Douglas"
-				last_name: "Adams"
-				email: "doug@sharelatex.com"
-				gravatar_url: "//gravatar/url/1"
-			@RoomController._getClientAttributes = sinon.stub().callsArgWith(1, null, @client.params)
-			@SocketManager.addClientToRoom = sinon.stub().callsArg(2)
-			@SocketManager.emitToRoom = sinon.stub()
-			@RoomController._addClientToRoom(@client, @room_id, @callback)
-
-		it "should add the client to the room", ->
-			@SocketManager.addClientToRoom
-				.calledWith(@client, @room_id)
-				.should.equal true
-
-		it "should tell the room that the client has been added", ->
-			@SocketManager.emitToRoom
-				.calledWith(@room_id, "userJoined", {
-					room:
-						id: @room_id
-					user:
-						id           : @client.params["id"]
-						first_name   : @client.params["first_name"]
-						last_name    : @client.params["last_name"]
-						email        : @client.params["email"]
-						gravatar_url : @client.params["gravatar_url"]
-				})
-				.should.equal true
-
-		it "should call the callback", ->
-			@callback.called.should.equal true
-
-

+ 0 - 75
services/chat/test/unit/coffee/Sockets/RealTimeEventManagerTests.coffee

@@ -1,75 +0,0 @@
-SandboxedModule = require('sandboxed-module')
-sinon = require('sinon')
-require('chai').should()
-modulePath = "../../../../app/js/Features/Sockets/RealTimeEventManager.js"
-
-describe "RealTimeEventManager", ->
-
-	beforeEach ->
-		@settings = 
-			redis:
-				web:
-					host: "host here"
-					port: "port here"
-					password: "password here"
-		@RealTimeEventManager = SandboxedModule.require modulePath, requires:
-			"redis": 
-				createClient: () ->
-					auth:->
-			"../../server" : io: @io = {}
-			"settings-sharelatex":@settings
-		@RealTimeEventManager.rclientPub = publish: sinon.stub()
-		@RealTimeEventManager.rclientSub =
-			subscribe: sinon.stub()
-			on: sinon.stub()
-		
-		@room_id = "room-id-here"
-		@message = "message-to-chat-here"
-		@payload = ["argument one", 42]
-
-	describe "emitToRoom", ->
-		beforeEach ->
-			@RealTimeEventManager.emitToRoom(@room_id, @message, @payload...)
-
-		it "should publish the message to redis", ->
-			@RealTimeEventManager.rclientPub.publish
-				.calledWith("chat-events", JSON.stringify(
-					room_id: @room_id,
-					message: @message
-					payload: @payload
-				))
-				.should.equal true
-			
-	describe "listenForChatEvents", ->
-		beforeEach ->
-			@RealTimeEventManager._processEditorEvent = sinon.stub()
-			@RealTimeEventManager.listenForChatEvents()
-
-		it "should subscribe to the chat-events channel", ->
-			@RealTimeEventManager.rclientSub.subscribe
-				.calledWith("chat-events")
-				.should.equal true
-
-		it "should process the events with _processEditorEvent", ->
-			@RealTimeEventManager.rclientSub.on
-				.calledWith("message", sinon.match.func)
-				.should.equal true
-
-	describe "_processEditorEvent", ->
-		describe "with a designated room", ->
-			beforeEach ->
-				@io.sockets =
-					in: sinon.stub().returns(emit: @emit = sinon.stub())
-				data = JSON.stringify
-					room_id: @room_id
-					message: @message
-					payload: @payload
-				@RealTimeEventManager._processEditorEvent("chat-events", data)
-
-			it "should send the message to all clients in the room", ->
-				@io.sockets.in
-					.calledWith(@room_id)
-					.should.equal true
-				@emit.calledWith(@message, @payload...).should.equal true
-
-