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

add notification backend and unit test
add notification init frontend

Henrique Santos 10 лет назад
Родитель
Сommit
476eaa8b84

+ 15 - 0
services/web/app/coffee/Features/Notifications/NotificationsController.coffee

@@ -0,0 +1,15 @@
+NotificationsHandler = require("./NotificationsHandler")
+logger = require("logger-sharelatex")
+
+module.exports =
+
+	getAllUnreadNotifications: (req, res)->
+		NotificationsHandler.getUserNotifications req.session.user._id, (err, unreadNotifications)->
+			res.send(unreadNotifications)
+
+	markNotificationAsRead: (req, res)->
+		user_id = req.session.user._id
+		notification_id = req.params.notification_id
+		NotificationsHandler.markAsRead user_id, notification_id, ->
+			res.send()
+		logger.log user_id:user_id, notification_id:notification_id, "mark notification as read"

+ 29 - 0
services/web/app/coffee/Features/Notifications/NotificationsHandler.coffee

@@ -0,0 +1,29 @@
+settings = require("settings-sharelatex")
+request = require("request")
+logger = require("logger-sharelatex")
+
+oneSecond = 1000
+module.exports = 
+
+	getUserNotifications: (user_id, callback)->
+		opts = 
+			uri: "#{settings.apis.notifications.url}/user/#{user_id}"
+			json: true
+			timeout: 2000
+		request.get opts, (err, res, unreadNotifications)->
+			statusCode =  if res? then res.statusCode else 500
+			if err? or statusCode != 200
+				e = new Error("something went wrong getting notifications, #{err}, #{statusCode}")
+				logger.err err:err
+				callback(e, [])
+			else
+				if !unreadNotifications?
+					unreadNotifications = []
+				callback(null, unreadNotifications)
+
+	markAsRead: (user_id, notification_id, callback)->
+		opts =
+			uri: "#{settings.apis.notifications.url}/user/#{user_id}/notification/#{notification_id}"
+			timeout:oneSecond
+		logger.log user_id:user_id, notification_id:notification_id, "send mark notification to notifications api"
+		request.del opts, callback

+ 10 - 11
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -8,6 +8,7 @@ metrics = require('../../infrastructure/Metrics')
 Project = require('../../models/Project').Project
 Project = require('../../models/Project').Project
 User = require('../../models/User').User
 User = require('../../models/User').User
 TagsHandler = require("../Tags/TagsHandler")
 TagsHandler = require("../Tags/TagsHandler")
+NotificationsHandler = require("../Notifications/NotificationsHandler")
 SubscriptionLocator = require("../Subscription/SubscriptionLocator")
 SubscriptionLocator = require("../Subscription/SubscriptionLocator")
 LimitationsManager = require("../Subscription/LimitationsManager")
 LimitationsManager = require("../Subscription/LimitationsManager")
 _ = require("underscore")
 _ = require("underscore")
@@ -16,8 +17,6 @@ SecurityManager = require("../../managers/SecurityManager")
 fs = require "fs"
 fs = require "fs"
 InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
 InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
 ProjectUpdateHandler = require("./ProjectUpdateHandler")
 ProjectUpdateHandler = require("./ProjectUpdateHandler")
-ReferencesSearchHandler = require("../ReferencesSearch/ReferencesSearchHandler")
-
 
 
 module.exports = ProjectController =
 module.exports = ProjectController =
 
 
@@ -127,6 +126,8 @@ module.exports = ProjectController =
 		async.parallel {
 		async.parallel {
 			tags: (cb)->
 			tags: (cb)->
 				TagsHandler.getAllTags user_id, cb
 				TagsHandler.getAllTags user_id, cb
+			notifications: (cb)->
+				NotificationsHandler.getUserNotifications user_id, cb
 			projects: (cb)->
 			projects: (cb)->
 				Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
 				Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
 			hasSubscription: (cb)->
 			hasSubscription: (cb)->
@@ -139,6 +140,7 @@ module.exports = ProjectController =
 					return next(err)
 					return next(err)
 				logger.log results:results, user_id:user_id, "rendering project list"
 				logger.log results:results, user_id:user_id, "rendering project list"
 				tags = results.tags[0]
 				tags = results.tags[0]
+				notifications = results.notifications
 				projects = ProjectController._buildProjectList results.projects[0], results.projects[1], results.projects[2]
 				projects = ProjectController._buildProjectList results.projects[0], results.projects[1], results.projects[2]
 				user = results.user
 				user = results.user
 				ProjectController._injectProjectOwners projects, (error, projects) ->
 				ProjectController._injectProjectOwners projects, (error, projects) ->
@@ -149,6 +151,7 @@ module.exports = ProjectController =
 						priority_title: true
 						priority_title: true
 						projects: projects
 						projects: projects
 						tags: tags
 						tags: tags
+						notifications: notifications
 						user: user
 						user: user
 						hasSubscription: results.hasSubscription[0]
 						hasSubscription: results.hasSubscription[0]
 					}
 					}
@@ -170,15 +173,15 @@ module.exports = ProjectController =
 			return res.render("general/closed", {title:"updating_site"})
 			return res.render("general/closed", {title:"updating_site"})
 
 
 		if req.session.user?
 		if req.session.user?
-			user_id = req.session.user._id
+			user_id = req.session.user._id 
 			anonymous = false
 			anonymous = false
 		else
 		else
 			anonymous = true
 			anonymous = true
 			user_id = 'openUser'
 			user_id = 'openUser'
-
+		
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
 		logger.log project_id:project_id, "loading editor"
 		logger.log project_id:project_id, "loading editor"
-
+	
 		async.parallel {
 		async.parallel {
 			project: (cb)->
 			project: (cb)->
 				Project.findPopulatedById project_id, cb
 				Project.findPopulatedById project_id, cb
@@ -195,7 +198,7 @@ module.exports = ProjectController =
 				SubscriptionLocator.getUsersSubscription user_id, cb
 				SubscriptionLocator.getUsersSubscription user_id, cb
 			activate: (cb)->
 			activate: (cb)->
 				InactiveProjectManager.reactivateProjectIfRequired project_id, cb
 				InactiveProjectManager.reactivateProjectIfRequired project_id, cb
-			markAsOpened: (cb)->
+			markAsOpened: (cb)-> 
 				#don't need to wait for this to complete
 				#don't need to wait for this to complete
 				ProjectUpdateHandler.markAsOpened project_id, ->
 				ProjectUpdateHandler.markAsOpened project_id, ->
 				cb()
 				cb()
@@ -207,7 +210,6 @@ module.exports = ProjectController =
 			user = results.user
 			user = results.user
 			subscription = results.subscription
 			subscription = results.subscription
 
 
-
 			daysSinceLastUpdated =  (new Date() - project.lastUpdated) /86400000
 			daysSinceLastUpdated =  (new Date() - project.lastUpdated) /86400000
 			logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
 			logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
 
 
@@ -217,10 +219,6 @@ module.exports = ProjectController =
 
 
 				if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
 				if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
 					allowedFreeTrial = !!subscription.freeTrial.allowed || true
 					allowedFreeTrial = !!subscription.freeTrial.allowed || true
-
-				# HACK: don't do it for now
-				ReferencesSearchHandler.indexProjectReferences project, ->  # don't need to wait on this
-
 				logger.log project_id:project_id, "rendering editor page"
 				logger.log project_id:project_id, "rendering editor page"
 				res.render 'project/editor',
 				res.render 'project/editor',
 					title:  project.name
 					title:  project.name
@@ -318,3 +316,4 @@ do generateThemeList = () ->
 		if file.slice(-2) == "js" and file.match(/^theme-/)
 		if file.slice(-2) == "js" and file.match(/^theme-/)
 			cleanName = file.slice(0,-3).slice(6)
 			cleanName = file.slice(0,-3).slice(6)
 			THEME_LIST.push cleanName
 			THEME_LIST.push cleanName
+

+ 12 - 12
services/web/app/coffee/router.coffee

@@ -16,6 +16,7 @@ ReferalController = require('./Features/Referal/ReferalController')
 ReferalMiddleware = require('./Features/Referal/ReferalMiddleware')
 ReferalMiddleware = require('./Features/Referal/ReferalMiddleware')
 AuthenticationController = require('./Features/Authentication/AuthenticationController')
 AuthenticationController = require('./Features/Authentication/AuthenticationController')
 TagsController = require("./Features/Tags/TagsController")
 TagsController = require("./Features/Tags/TagsController")
+NotificationsController = require("./Features/Notifications/NotificationsController")
 CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter')
 CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter')
 UserInfoController = require('./Features/User/UserInfoController')
 UserInfoController = require('./Features/User/UserInfoController')
 UserController = require("./Features/User/UserController")
 UserController = require("./Features/User/UserController")
@@ -37,7 +38,6 @@ RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
 RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
 RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
 InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
 InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
 ContactRouter = require("./Features/Contacts/ContactRouter")
 ContactRouter = require("./Features/Contacts/ContactRouter")
-ReferencesSearchController = require('./Features/ReferencesSearch/ReferencesSearchController')
 
 
 logger = require("logger-sharelatex")
 logger = require("logger-sharelatex")
 _ = require("underscore")
 _ = require("underscore")
@@ -47,7 +47,7 @@ module.exports = class Router
 		if !Settings.allowPublicAccess
 		if !Settings.allowPublicAccess
 			webRouter.all '*', AuthenticationController.requireGlobalLogin
 			webRouter.all '*', AuthenticationController.requireGlobalLogin
 
 
-
+		
 		webRouter.get  '/login', UserPagesController.loginPage
 		webRouter.get  '/login', UserPagesController.loginPage
 		AuthenticationController.addEndpointToLoginWhitelist '/login'
 		AuthenticationController.addEndpointToLoginWhitelist '/login'
 
 
@@ -68,18 +68,18 @@ module.exports = class Router
 		StaticPagesRouter.apply(webRouter, apiRouter)
 		StaticPagesRouter.apply(webRouter, apiRouter)
 		RealTimeProxyRouter.apply(webRouter, apiRouter)
 		RealTimeProxyRouter.apply(webRouter, apiRouter)
 		ContactRouter.apply(webRouter, apiRouter)
 		ContactRouter.apply(webRouter, apiRouter)
-
+		
 		Modules.applyRouter(webRouter, apiRouter)
 		Modules.applyRouter(webRouter, apiRouter)
 
 
 
 
 		if Settings.enableSubscriptions
 		if Settings.enableSubscriptions
 			webRouter.get  '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
 			webRouter.get  '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
-
+		
 		webRouter.get '/blog', BlogController.getIndexPage
 		webRouter.get '/blog', BlogController.getIndexPage
 		webRouter.get '/blog/*', BlogController.getPage
 		webRouter.get '/blog/*', BlogController.getPage
-
+		
 		webRouter.get '/user/activate', UserPagesController.activateAccountPage
 		webRouter.get '/user/activate', UserPagesController.activateAccountPage
-
+		
 		webRouter.get  '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
 		webRouter.get  '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
 		webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
 		webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
 		webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
 		webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
@@ -134,6 +134,9 @@ module.exports = class Router
 		webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
 		webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
 		webRouter.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
 		webRouter.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
 
 
+		webRouter.get '/notifications', AuthenticationController.requireLogin(), NotificationsController.getAllUnreadNotifications
+		webRouter.delete '/notifications/:notification_id', AuthenticationController.requireLogin(), NotificationsController.markNotificationAsRead	
+
 		# Deprecated in favour of /internal/project/:project_id but still used by versioning
 		# Deprecated in favour of /internal/project/:project_id but still used by versioning
 		apiRouter.get  '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
 		apiRouter.get  '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
 
 
@@ -159,7 +162,7 @@ module.exports = class Router
 
 
 		apiRouter.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
 		apiRouter.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
 		apiRouter.delete '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
 		apiRouter.delete '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
-
+		
 		apiRouter.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
 		apiRouter.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
 		apiRouter.delete '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
 		apiRouter.delete '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
 
 
@@ -168,12 +171,9 @@ module.exports = class Router
 
 
 		webRouter.get  "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
 		webRouter.get  "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
 		webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
 		webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
-
+		
 		webRouter.get  /learn(\/.*)?/, WikiController.getPage
 		webRouter.get  /learn(\/.*)?/, WikiController.getPage
 
 
-		webRouter.post "/project/:Project_id/references", SecurityManager.requestCanAccessProject, ReferencesSearchController.indexFile
-		webRouter.get "/project/:Project_id/references/keys", SecurityManager.requestCanAccessProject, ReferencesSearchController.getKeys
-
 		#Admin Stuff
 		#Admin Stuff
 		webRouter.get  '/admin', SecurityManager.requestIsAdmin, AdminController.index
 		webRouter.get  '/admin', SecurityManager.requestIsAdmin, AdminController.index
 		webRouter.get  '/admin/user', SecurityManager.requestIsAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
 		webRouter.get  '/admin/user', SecurityManager.requestIsAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
@@ -192,7 +192,7 @@ module.exports = class Router
 
 
 		apiRouter.get '/status', (req,res)->
 		apiRouter.get '/status', (req,res)->
 			res.send("websharelatex is up")
 			res.send("websharelatex is up")
-
+		
 
 
 		webRouter.get '/health_check', HealthCheckController.check
 		webRouter.get '/health_check', HealthCheckController.check
 		webRouter.get '/health_check/redis', HealthCheckController.checkRedis
 		webRouter.get '/health_check/redis', HealthCheckController.checkRedis

+ 3 - 1
services/web/app/views/project/list.jade

@@ -9,7 +9,8 @@ block content
 	script(type="text/javascript").
 	script(type="text/javascript").
 		window.data = {
 		window.data = {
 			projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},
 			projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},
-			tags: !{JSON.stringify(tags).replace(/\//g, '\\/')}
+			tags: !{JSON.stringify(tags).replace(/\//g, '\\/')},
+			notifications: !{JSON.stringify(notifications).replace(/\//g, '\\/')}
 		};
 		};
 		window.algolia = {
 		window.algolia = {
 			institutions: {
 			institutions: {
@@ -19,6 +20,7 @@ block content
 		};
 		};
 
 
 	.content.content-alt(ng-controller="ProjectPageController")
 	.content.content-alt(ng-controller="ProjectPageController")
+		include ./list/notifications
 		.container
 		.container
 			.row(ng-cloak)
 			.row(ng-cloak)
 				span(ng-show="first_sign_up == 'default' || projects.length > 0")
 				span(ng-show="first_sign_up == 'default' || projects.length > 0")

+ 18 - 0
services/web/app/views/project/list/notifications.jade

@@ -0,0 +1,18 @@
+.notifications(ng-controller="NotificationsController")
+	.row.row-spaced
+		.col-xs-12
+			ul.list-unstyled.notifications-list.structured-list(
+				select-all-list,
+				ng-if="notifications.length > 0",
+				ng-cloak
+			)
+				li.notification_entry.container-fluid(
+					ng-repeat="unreadNotification in notifications",
+				)
+					.row
+						.col-xs-6
+							span {{unreadNotification._id}}
+						.col-xs-2
+							span.owner {{unreadNotification.user_id}}
+						.col-xs-4
+							span.last-modified {{unreadNotification.templateKey}}

+ 2 - 2
services/web/config/settings.defaults.coffee

@@ -106,6 +106,8 @@ module.exports =
 			url: "http://localhost:3036"
 			url: "http://localhost:3036"
 		sixpack:
 		sixpack:
 			url: ""
 			url: ""
+		notifications:
+			url: "http://localhost:3033" 
 			
 			
 	templates:
 	templates:
 		user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
 		user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
@@ -143,8 +145,6 @@ module.exports =
 		versioning: true
 		versioning: true
 		compileTimeout: 60
 		compileTimeout: 60
 		compileGroup: "standard"
 		compileGroup: "standard"
-		references: true
-		templates: true
 
 
 	plans: plans = [{
 	plans: plans = [{
 		planCode: "personal"
 		planCode: "personal"

+ 1 - 0
services/web/public/coffee/main/project-list/index.coffee

@@ -2,6 +2,7 @@ define [
 	"main/project-list/project-list"
 	"main/project-list/project-list"
 	"main/project-list/modal-controllers"
 	"main/project-list/modal-controllers"
 	"main/project-list/tag-controllers"
 	"main/project-list/tag-controllers"
+	"main/project-list/notifications-controller"
 	"main/project-list/queued-http"
 	"main/project-list/queued-http"
 	"main/project-list/left-hand-menu-promo-controller"
 	"main/project-list/left-hand-menu-promo-controller"
 ], () ->
 ], () ->

+ 5 - 0
services/web/public/coffee/main/project-list/notifications-controller.coffee

@@ -0,0 +1,5 @@
+define [
+	"base"
+], (App) ->
+
+	App.controller "NotificationsController", ($scope) ->

+ 1 - 0
services/web/public/coffee/main/project-list/project-list.coffee

@@ -5,6 +5,7 @@ define [
 	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, sixpack) ->
 	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, sixpack) ->
 		$scope.projects = window.data.projects
 		$scope.projects = window.data.projects
 		$scope.tags = window.data.tags
 		$scope.tags = window.data.tags
+		$scope.notifications = window.data.notifications
 		$scope.allSelected = false
 		$scope.allSelected = false
 		$scope.selectedProjects = []
 		$scope.selectedProjects = []
 		$scope.filter = "all"
 		$scope.filter = "all"

+ 39 - 0
services/web/test/UnitTests/coffee/Notifications/NotificationsControllerTests.coffee

@@ -0,0 +1,39 @@
+SandboxedModule = require('sandboxed-module')
+assert = require('assert')
+require('chai').should()
+sinon = require('sinon')
+modulePath = require('path').join __dirname, '../../../../app/js/Features/Notifications/NotificationsController.js'
+
+
+describe 'NotificationsController', ->
+	user_id = "123nd3ijdks"
+	notification_id = "123njdskj9jlk"
+
+	beforeEach ->
+		@handler = 
+			getUserNotifications: sinon.stub().callsArgWith(1)
+			markAsRead: sinon.stub().callsArgWith(2)
+		@controller = SandboxedModule.require modulePath, requires:
+			"./NotificationsHandler":@handler
+			'logger-sharelatex':
+				log:->
+				err:->
+		@req =
+			params:
+				notification_id:notification_id
+			session:
+				user:
+					_id:user_id
+
+	it 'should ask the handler for all unread notifications', (done)->
+		allNotifications = [{_id: notification_id, user_id: user_id}]
+		@handler.getUserNotifications = sinon.stub().callsArgWith(1, null, allNotifications)
+		@controller.getAllUnreadNotifications @req, send:(body)=>
+			body.should.equal allNotifications
+			@handler.getUserNotifications.calledWith(user_id).should.equal true
+			done()
+
+	it 'should send a delete request when a delete has been received to mark a notification', (done)->
+		@controller.markNotificationAsRead @req, send:=>
+			@handler.markAsRead.calledWith(user_id, notification_id).should.equal true
+			done()

+ 48 - 0
services/web/test/UnitTests/coffee/Notifications/NotificationsHandlerTests.coffee

@@ -0,0 +1,48 @@
+SandboxedModule = require('sandboxed-module')
+assert = require('chai').assert
+require('chai').should()
+sinon = require('sinon')
+modulePath = require('path').join __dirname, '../../../../app/js/Features/Notifications/NotificationsHandler.js'
+_ = require('underscore')
+
+
+describe 'NotificationsHandler', ->
+	user_id = "123nd3ijdks"
+	notification_id = "123njdskj9jlk"
+	notificationUrl = "notification.sharelatex.testing"
+
+	beforeEach ->
+		@request = 
+			post: sinon.stub().callsArgWith(1)
+			del: sinon.stub().callsArgWith(1)
+			get: sinon.stub()
+		@handler = SandboxedModule.require modulePath, requires:
+			"settings-sharelatex": apis:{notifications:{url:notificationUrl}}
+			"request":@request
+			'logger-sharelatex':
+				log:->
+				err:->
+
+	describe "getUserNotifications", ->
+		it 'should get unread notifications', (done)->
+			stubbedNotifications = [{_id: notification_id, user_id: user_id}]
+			@request.get.callsArgWith(1, null, {statusCode:200}, stubbedNotifications)
+			@handler.getUserNotifications user_id, (err, unreadNotifications)=>
+				stubbedNotifications.should.deep.equal unreadNotifications
+				getOpts =
+					uri: "#{notificationUrl}/user/#{user_id}"
+					json:true
+					timeout:2000
+				@request.get.calledWith(getOpts).should.equal true
+				done()
+
+		it 'should return empty arrays if there are no notifications', ->
+			@request.get.callsArgWith(1, null, {statusCode:200}, null)
+			@handler.getUserNotifications user_id, (err, unreadNotifications)=>
+				unreadNotifications.length.should.equal 0
+
+	describe "markAsRead", ->
+		it 'should send a delete request when a delete has been received to mark a notification', (done)->
+			@handler.markAsRead user_id, notification_id, =>
+				@request.del.calledWith({uri:"#{notificationUrl}/user/#{user_id}/notification/#{notification_id}", timeout:1000}).should.equal true
+				done()