From 27a33011aca1ac88676c73233cd570b0768acc6b Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Tue, 7 Jan 2014 21:18:13 +0200 Subject: [PATCH 1/6] .gitignore --- .gitignore | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f3647c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.sublime-* +.directory + +pids +logs +results + +npm-debug.log +node_modules From e9d90d4b52e071909cb09a4f67710c45c13dfaca Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Mon, 13 Jan 2014 17:25:10 +0200 Subject: [PATCH 2/6] Message separated to Message and Share, authentication logic moved to User model --- models/server/Message.js | 3 +- models/server/Share.js | 10 ++++ models/server/User.js | 98 ++++++++++++++++++++++++--------------- package.json | 7 ++- routes/api/users.js | 28 ++++++----- tests/api/user.spec.js | 43 ++++++++--------- tests/helpers/index.js | 40 ++++++++-------- tests/models/user.spec.js | 67 +++++++++++++++++++------- 8 files changed, 185 insertions(+), 111 deletions(-) create mode 100644 models/server/Share.js diff --git a/models/server/Message.js b/models/server/Message.js index 82c34bd..c658eb4 100644 --- a/models/server/Message.js +++ b/models/server/Message.js @@ -1,7 +1,8 @@ var mongoose = require("mongoose"); var Message = new mongoose.Schema({ + author: { type: type: mongoose.Schema.Types.ObjectId, required: true }, body: { type: String, unique: true, required: true } -}) +}); module.exports = mongoose.model("Message", Message) \ No newline at end of file diff --git a/models/server/Share.js b/models/server/Share.js new file mode 100644 index 0000000..188ba95 --- /dev/null +++ b/models/server/Share.js @@ -0,0 +1,10 @@ +var mongoose = require("mongoose"); + +var Share = new mongoose.Schema({ + author: { type: mongoose.Schema.Types.ObjectId, required: true }, + message: { type: mongoose.Schema.Types.ObjectId, required: true }, + date: { type: Date, default: Date.now } +}); +Share.index({author: 1, message: 1}); //each message can be shared only one by a user + +module.exports = mongoose.model("Share", Share); \ No newline at end of file diff --git a/models/server/User.js b/models/server/User.js index f1e6a20..9ca6cee 100644 --- a/models/server/User.js +++ b/models/server/User.js @@ -1,50 +1,72 @@ +"use strict"; var mongoose = require("mongoose"); -var crypto = require("crypto"); +var pw = require("credential"); var User = new mongoose.Schema({ username: { type: String, unique: true, required: true }, - password: { type: String, required: true, select: false }, - messages: [{ type: mongoose.Schema.Types.ObjectId }] -}) + // add Twitter/Facebook authentication in the future? + authType: { type: String, default: "local" }, + credentials: String +}); -var hashPassword = function(value) { - var md5sum; - md5sum = crypto.createHash('md5'); - md5sum.update(value); - return md5sum.digest('hex'); -} - -User.pre("save", function(next) { - if (!this.isModified('password')) - return next(); - this.password = hashPassword(this.password); - return next(); -}) - -User.static("hashPassword", hashPassword) +/* + { + hash: String, + salt: String, + keyLength: Number, + hashMethod: String, + workUnits: Number + } +*/ -User.static("available", function(email, username, callback) { - this.findOne({ username: username }, callback); -}) +User.static("register", function (credentials, done) { + if (typeof credentials.username !== "string" || !credentials.username) { + done(new Error("user must have unique not-empty username")); + return; + } + module.exports.available(credentials.username, function (err, isAvailable) { + if (err) return done(err); + if (!isAvailable) return done(new Error("Username not available")); + //if authType === "local" + credentials.authType = "local"; + registerLocal(credentials, function (err, user) { + if (err) return done(err); + module.exports.create(user, done); + }); + }); +}); -User.static("findOneByUsernamePassword", function(username, password, callback) { - var pattern = { - username: username, - password: this.hashPassword(password) +var registerLocal = function (user, done) { + if (typeof user.password !== "string" || !user.password) { + done(new Error("user must have not-empty password")); + return; } - this.findOne(pattern).exec(callback); -}) + pw.hash(user.password, function (err, hash) { + if (err) return done(err); + delete user.password; + user.credentials = hash; + done(false, user); + }); +}; -User.method("sendMessage", function(message, callback){ - // ToDo - - // 1. check is message already stored in DB (has _id property) - // 1.1 create the message if not stored yet +User.static("authenticate", function (input, done) { + module.exports.findOne({"username": input.username}, function (err, user) { + if (err) return done(err); + if (!user) return done(false, false); + //if authType === "local" + pw.verify(user.credentials, input.password, function (err, success) { + if (err) return done(err); + if (!success) return done(false, false); + done(false, user._id); + }); + }); +}); - // 2. use message._id to find users who send the message before - // 2.1. increment those users points - // - // 3. add the message._id to current user's messages -}) +User.static("available", function (username, callback) { + this.findOne({ "username": username }, function (err, user) { + if (err) callback(err); + callback(false, !user); + }); +}); module.exports = mongoose.model("User", User); \ No newline at end of file diff --git a/package.json b/package.json index db93afb..f7fcdec 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ "mongoose": "~3.8.4", "connect-mongo": "~0.4.0", "express": "~3.4.7", - "request": "~2.31.0" + "request": "~2.31.0", + "credential": "~0.2.5" + }, + "devDependencies": { + "jasmine-node": "~1.12.0" }, - "devDependencies": {}, "engines": { "node": ">=0.10.24" } diff --git a/routes/api/users.js b/routes/api/users.js index bf8ed16..6278e17 100644 --- a/routes/api/users.js +++ b/routes/api/users.js @@ -3,26 +3,30 @@ var User = require("../../models/server/User"); module.exports = { logout: function(req, res) { req.session.destroy(); - res.send({result: true}) + res.send({result: true}); }, login: function(req, res) { - if(!req.body.username || !req.body.password) return res.send(400) - User.findOneByUsernamePassword(req.body.username, req.body.password, function(err, user){ + if(!req.body.username || !req.body.password) return res.send(400); + User.authenticate(req.body, function(err, userId){ if(!err) { - if(user) - req.session.userId = user.id; - res.send({result: user}) + if(userId) { + req.session.userId = userId; + res.send({result: { "_id": userId, "username": req.body.username } }); + } else { + //TODO: send redirect to login page + res.send(403, {result: "Invalid username or password"}); + } } else - res.send(500, {result: err}) + res.send(500, {result: err}); }); }, register: function(req, res) { - User.create(req.body, function(err, user){ + User.register(req.body, function(err, user){ if(!err) { req.session.userId = user.id; - res.send({result: user}) + res.send({result: user}); } else - res.send(500, {result: err}) - }) + res.send(500, {result: err}); + }); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/tests/api/user.spec.js b/tests/api/user.spec.js index 9147e71..ae05270 100644 --- a/tests/api/user.spec.js +++ b/tests/api/user.spec.js @@ -1,7 +1,8 @@ describe("user api", function(){ - var request = require("request") - var helpers = require("../helpers") - it("starts", helpers.startApiHttpServer) + var request = require("request"); + var helpers = require("../helpers"); + + it("starts", helpers.startApiHttpServer); it("registers new user", function(next){ request.post({ @@ -11,21 +12,21 @@ describe("user api", function(){ password: "test" } }, function(err, res, body){ - expect(res.statusCode).toBe(200) - expect(body.result._id).toBeDefined() - next() - }) - }) + expect(res.statusCode).toBe(200); + expect(body.result._id).toBeDefined(); + next(); + }); + }); it("logout user", function(next){ request.get({ uri: helpers.apiendpoint+"/users/me/logout", json: {} }, function(err, res, body){ - expect(res.statusCode).toBe(200) - next() - }) - }) + expect(res.statusCode).toBe(200); + next(); + }); + }); it("login user", function(next){ request.get({ @@ -35,14 +36,14 @@ describe("user api", function(){ password: "test" } }, function(err, res, body){ - expect(res.statusCode).toBe(200) - expect(body.result._id).toBeDefined() - expect(body.result.username).toBe("testuser") - expect(body.result.password).not.toBeDefined() - next() - }) - }) + expect(res.statusCode).toBe(200); + expect(body.result._id).toBeDefined(); + expect(body.result.username).toBe("testuser"); + expect(body.result.password).not.toBeDefined(); + next(); + }); + }); - it("stops", helpers.stopApiHttpServer) + it("stops", helpers.stopApiHttpServer); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/tests/helpers/index.js b/tests/helpers/index.js index ce2b0c2..7bca26c 100644 --- a/tests/helpers/index.js +++ b/tests/helpers/index.js @@ -1,26 +1,22 @@ -var mongoose = require("mongoose") -var Socialpower = require("../../api") +var mongoose = require("mongoose"); +var Socialpower = require("../../api"); -module.exports.apiendpoint = "http://127.0.0.1:3002" +module.exports.apiendpoint = "http://127.0.0.1:3002"; module.exports.connectMongoose = function(next){ - mongoose.connect("localhost/socialpower-test", function(){ - mongoose.connection.db.dropDatabase(function(){ - mongoose.disconnect(function(){ - mongoose.connect("localhost/socialpower-test", next) - }) - }) - }) -} + mongoose.connect("localhost/socialpower-test", next); +}; module.exports.disconnectMongoose = function(next){ - mongoose.disconnect(next) -} + mongoose.connection.db.dropDatabase(function(){ + mongoose.disconnect(next); + }); +}; module.exports.startApiHttpServer = function(next){ module.exports.connectMongoose(function(){ mongoose.disconnect(function(){ - module.exports.api = new Socialpower() + module.exports.api = new Socialpower(); module.exports.api.start({ "port": "3002", "db": { @@ -28,10 +24,14 @@ module.exports.startApiHttpServer = function(next){ "host": "localhost" }, "secret": "socialpower-test" - }, next) - }) - }) -} + }, next); + }); + }); +}; module.exports.stopApiHttpServer = function(next){ - module.exports.api.stop(next) -} \ No newline at end of file + module.exports.api.stop(function () { + module.exports.connectMongoose(function () { + module.exports.disconnectMongoose(next); + }); + }); +}; \ No newline at end of file diff --git a/tests/models/user.spec.js b/tests/models/user.spec.js index 2a0fb64..8998ad9 100644 --- a/tests/models/user.spec.js +++ b/tests/models/user.spec.js @@ -1,20 +1,53 @@ describe("User model", function(){ - var helpers = require("../helpers") - var User = require("../../models/server/User") + "use strict"; + var helpers = require("../helpers"); + var User = require("../../models/server/User"); + var pw = require("credential"); - beforeEach(helpers.connectMongoose) - afterEach(helpers.disconnectMongoose) + beforeEach(helpers.connectMongoose); + afterEach(helpers.disconnectMongoose); - it("creates with hashed password", function(next){ - User.create({ - "username": "test", - "password": "123" - }, function(err, user){ - expect(user.password).not.toBe("123") - User.findById(user._id, {fields: "+password"}, function(err, user){ - expect(user.password).not.toBe("123") - next() - }) - }) - }) -}) \ No newline at end of file + it("creates a user object", function () { + expect(new User({ "username": "test", "password": "secret" })).toEqual(jasmine.any(User)); + }); + + it("register user with salted hash password", function(next){ + User.register({ "username": "test", "password": "secret" }, function(err, user){ + expect(user.password).toBeUndefined(); + expect(user.credentials.hash).not.toBe("secret"); + next(); + }); + }); + + describe("with user is regustered", function () { + beforeEach(function (next) { + User.register({ "username": "test", "password": "secret" }, function () { + next(); + }); + }); + + it("authenticates a user based on username and password", function (next) { + User.authenticate({ "username": "test", "password": "secret" }, function (err, success) { + expect(err).toBeFalsy(); + expect(success).toBeTruthy(); + next(); + }); + }); + + it("fails with Invalid username or password for bad password", function (next) { + User.authenticate({ "username": "test", "password": "secret " }, function (err, success) { + expect(err).toBeFalsy(); + expect(success).toBeFalsy(); + next(); + }); + }); + + it("fails with Invalid username or password for bad username", function (next) { + User.authenticate({ "username": "test", "password": "secret " }, function (err, success) { + expect(err).toBeFalsy(); + expect(success).toBeFalsy(); + next(); + }); + }); + }); +}); \ No newline at end of file From e464df2d0283a8015f2681d7c2149b8e166a223f Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Mon, 13 Jan 2014 17:31:02 +0200 Subject: [PATCH 3/6] bdd, test is changed so that each it is independent --- tests/api/user.spec.js | 78 ++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/tests/api/user.spec.js b/tests/api/user.spec.js index ae05270..b7ad1d8 100644 --- a/tests/api/user.spec.js +++ b/tests/api/user.spec.js @@ -2,7 +2,12 @@ describe("user api", function(){ var request = require("request"); var helpers = require("../helpers"); - it("starts", helpers.startApiHttpServer); + beforeEach(function (next) { + helpers.startApiHttpServer(next); + }); + afterEach(function (next) { + helpers.stopApiHttpServer(next); + }); it("registers new user", function(next){ request.post({ @@ -18,32 +23,55 @@ describe("user api", function(){ }); }); - it("logout user", function(next){ - request.get({ - uri: helpers.apiendpoint+"/users/me/logout", - json: {} - }, function(err, res, body){ - expect(res.statusCode).toBe(200); - next(); + describe("user registered and logged in", function () { + beforeEach(function (next) { + request.post({ + uri: helpers.apiendpoint+"/users/register", + json: { + username: "testuser", + password: "test" + } + }, function(err, res, body){ + next(); + }); }); - }); - it("login user", function(next){ - request.get({ - uri: helpers.apiendpoint+"/users/me/login", - json: { - username: "testuser", - password: "test" - } - }, function(err, res, body){ - expect(res.statusCode).toBe(200); - expect(body.result._id).toBeDefined(); - expect(body.result.username).toBe("testuser"); - expect(body.result.password).not.toBeDefined(); - next(); + it("logout user", function(next){ + request.get({ + uri: helpers.apiendpoint+"/users/me/logout", + json: {} + }, function(err, res, body){ + expect(res.statusCode).toBe(200); + next(); + }); }); - }); - it("stops", helpers.stopApiHttpServer); - + describe("and logged out", function () { + beforeEach(function (next) { + request.get({ + uri: helpers.apiendpoint+"/users/me/logout", + json: {} + }, function(err, res, body){ + next(); + }); + }); + + it("login user", function(next){ + request.get({ + uri: helpers.apiendpoint+"/users/me/login", + json: { + username: "testuser", + password: "test" + } + }, function(err, res, body){ + expect(res.statusCode).toBe(200); + expect(body.result._id).toBeDefined(); + expect(body.result.username).toBe("testuser"); + expect(body.result.password).not.toBeDefined(); + next(); + }); + }); + }); + + }); }); \ No newline at end of file From 87bd5887d7bc6fea986e62c9e5be59ee6a01d6b2 Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Tue, 14 Jan 2014 19:43:29 +0200 Subject: [PATCH 4/6] latest --- models/server/Activity.js | 34 +++++++++++++++++++ models/server/Message.js | 4 +-- models/server/Share.js | 2 ++ routes/api/activity.js | 34 +++++++++++++++++++ routes/api/messages.js | 11 ------- routes/index.js | 9 +++-- tests/api/activity.spec.js | 67 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 models/server/Activity.js create mode 100644 routes/api/activity.js delete mode 100644 routes/api/messages.js create mode 100644 tests/api/activity.spec.js diff --git a/models/server/Activity.js b/models/server/Activity.js new file mode 100644 index 0000000..2573c14 --- /dev/null +++ b/models/server/Activity.js @@ -0,0 +1,34 @@ +"use strict"; + +var Message = require("./Message"); +var Share = require("./Share"); + +module.exports.share = function (message, done) { + Message.create(message, function (err, msg) { + if (err) return done(err); + module.exports.reshare({ message: msg._id, author: message.author }, done); + }); +}; + +module.exports.reshare = function (share, done) { + Share.create(share, done); +}; + +module.exports.list = function (conditions, done) { + var results = {}; + Share.find(conditions).sort("-date").exec(function (err, shares) { + if (err) return done(err); + results.shares = shares; + Share.find(conditions).distinct("message", function (err, messageIds) { + if (err) return done(err); + Message.find({ _id:{ $in:shares } }, function (err, messages) { + if (err) return done(err); + results.messages = {}; + for (var i = 0; i < messages.length; i ++) { + results.messages[messages[i].id] = messages[i]; + } + done(false, results); + }); + }); + }); +}; \ No newline at end of file diff --git a/models/server/Message.js b/models/server/Message.js index c658eb4..c046e90 100644 --- a/models/server/Message.js +++ b/models/server/Message.js @@ -1,8 +1,8 @@ var mongoose = require("mongoose"); var Message = new mongoose.Schema({ - author: { type: type: mongoose.Schema.Types.ObjectId, required: true }, + author: { type: mongoose.Schema.Types.ObjectId, required: true }, body: { type: String, unique: true, required: true } }); -module.exports = mongoose.model("Message", Message) \ No newline at end of file +module.exports = mongoose.model("Message", Message); \ No newline at end of file diff --git a/models/server/Share.js b/models/server/Share.js index 188ba95..7e96585 100644 --- a/models/server/Share.js +++ b/models/server/Share.js @@ -1,6 +1,8 @@ var mongoose = require("mongoose"); var Share = new mongoose.Schema({ + //parent is the User._id of the user whoose post was reshared. For original posts it is empty + parent: { type: mongoose.Schema.Types.ObjectId, required: false }, author: { type: mongoose.Schema.Types.ObjectId, required: true }, message: { type: mongoose.Schema.Types.ObjectId, required: true }, date: { type: Date, default: Date.now } diff --git a/routes/api/activity.js b/routes/api/activity.js new file mode 100644 index 0000000..d65ee12 --- /dev/null +++ b/routes/api/activity.js @@ -0,0 +1,34 @@ +"use strict"; +var Activity = require("../../models/server/Activity"); +module.exports = { + list: function (req, res) { + Activity.list(req.body /* conditions */, function (err, activity) { + if (err) { + res.send(500, {result: err}); + } + res.send(200, activity); + }); + }, + share: function(req, res) { + var message = { + author: req.session.userId, + body: req.body.body // String + }; + Activity.share(message, function (err, share) { + if (err) { + res.send(500, {result: err}); + } + res.send(200, share); + }); + }, + reshare: function (req, res) { + var share = req.body; // { parent: id, message: id, [date: Date]} + share.author = req.session.userId; + Activity.reshare(share, function (err, share) { + if (err) { + res.send(500, {result: err}); + } + res.send(200, share); + }); + } +}; \ No newline at end of file diff --git a/routes/api/messages.js b/routes/api/messages.js deleted file mode 100644 index 7927681..0000000 --- a/routes/api/messages.js +++ /dev/null @@ -1,11 +0,0 @@ -var User = require("../../models/server/User") -module.exports = { - send: function(req, res, next) { - User.findById(req.session.userId, function(err, user){ - user.sendMessage(req.body, function(err, response){ - if(err) return res.send(500, {result: err}) - res.send(response) - }) - }) - } -} \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 847ea88..acb8390 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,5 +1,8 @@ module.exports.mount = function(app){ - app.get("/users/me/logout", require("./api/users").logout) - app.get("/users/me/login", require("./api/users").login) - app.post("/users/register", require("./api/users").register) + app.get("/users/me/logout", require("./api/users").logout); + app.get("/users/me/login", require("./api/users").login); + app.post("/users/register", require("./api/users").register); + app.get("/activity/list", require("./api/activity").list); + app.post("/activity/share", require("./api/activity").share); + app.post("/activity/reshare", require("./api/activity").reshare); } \ No newline at end of file diff --git a/tests/api/activity.spec.js b/tests/api/activity.spec.js new file mode 100644 index 0000000..28232bb --- /dev/null +++ b/tests/api/activity.spec.js @@ -0,0 +1,67 @@ +describe("activities", function () { + var request = require("request"); + var helpers = require("../helpers"); + + beforeEach(function (next) { + helpers.startApiHttpServer(next); + }); + afterEach(function (next) { + helpers.stopApiHttpServer(next); + }); + + it("lists nothing for empty database", function (next) { + request.get({ + uri: helpers.apiendpoint + "/activity/list", + json: {} //all + }, function(err, res, body){ + expect(res.statusCode).toBe(200); + expect(body.shares).toEqual([]); + expect(body.messages).toEqual({}); + next(); + }); + }); + + + + describe("registered and logged in `testuser`", function () { + var cookieJar = request.jar(); + beforeEach(function (next) { + //register + login + request.post({ + uri: helpers.apiendpoint+"/users/register", + json: { + username: "testuser", + password: "test" + }, + jar: cookieJar + }, function(err, res, body){ + next(); + }); + }); + + it("adds a new publication", function (next) { + request.post({ + uri: helpers.apiendpoint + "/activity/share", + json: { "body": "A simple text message" }, + jar: cookieJar + }, function (err, res, body) { + expect(err).toBeFalsy(); + expect(body.message).toBeTruthy(); + next(); + }); + }); + + // describe("with a publication", function () { + // beforeEach(function (next) { + // request.post({ + // uri: helpers.apiendpoint + "/activity/share", + // json: { "text" : "A simple text message" }, + // jar: cookieJar + // }, function (err, res, body) { + // next(); + // }); + // }); + + // }); + }); +}); \ No newline at end of file From e4888749fc134f0bd62de2c84ce183dcba11cd92 Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Tue, 14 Jan 2014 20:08:28 +0200 Subject: [PATCH 5/6] and with the proper variable name :-D --- models/server/Activity.js | 2 +- tests/api/activity.spec.js | 36 +++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/models/server/Activity.js b/models/server/Activity.js index 2573c14..8131ae1 100644 --- a/models/server/Activity.js +++ b/models/server/Activity.js @@ -21,7 +21,7 @@ module.exports.list = function (conditions, done) { results.shares = shares; Share.find(conditions).distinct("message", function (err, messageIds) { if (err) return done(err); - Message.find({ _id:{ $in:shares } }, function (err, messages) { + Message.find({ _id:{ $in:messageIds } }, function (err, messages) { if (err) return done(err); results.messages = {}; for (var i = 0; i < messages.length; i ++) { diff --git a/tests/api/activity.spec.js b/tests/api/activity.spec.js index 28232bb..0f3a702 100644 --- a/tests/api/activity.spec.js +++ b/tests/api/activity.spec.js @@ -51,17 +51,31 @@ describe("activities", function () { }); }); - // describe("with a publication", function () { - // beforeEach(function (next) { - // request.post({ - // uri: helpers.apiendpoint + "/activity/share", - // json: { "text" : "A simple text message" }, - // jar: cookieJar - // }, function (err, res, body) { - // next(); - // }); - // }); + describe("with a publication", function () { + beforeEach(function (next) { + request.post({ + uri: helpers.apiendpoint + "/activity/share", + json: { "body": "A simple text message" }, + jar: cookieJar + }, function (err, res, body) { + next(); + }); + }); + + it("can list the publication", function (next) { + request.get({ + uri: helpers.apiendpoint + "/activity/list", + json: {}, + jar: cookieJar + }, function(err, res, body){ + console.log("list result:", body); + expect(res.statusCode).toBe(200); + expect(body.shares.length).toEqual(1); + expect(Object.keys(body.messages).length).toEqual(1); + next(); + }); + }); - // }); + }); }); }); \ No newline at end of file From f04de80e3d4bb4284f46ed6d55cd8fb6f1cade39 Mon Sep 17 00:00:00 2001 From: Valeri Bogdanov Date: Tue, 14 Jan 2014 21:33:18 +0200 Subject: [PATCH 6/6] test improvement --- models/server/Share.js | 2 +- tests/api/activity.spec.js | 66 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/models/server/Share.js b/models/server/Share.js index 7e96585..a0dfa3c 100644 --- a/models/server/Share.js +++ b/models/server/Share.js @@ -7,6 +7,6 @@ var Share = new mongoose.Schema({ message: { type: mongoose.Schema.Types.ObjectId, required: true }, date: { type: Date, default: Date.now } }); -Share.index({author: 1, message: 1}); //each message can be shared only one by a user +Share.index({author: 1, message: 1}, {unique: true}); //each message can be shared only one by a user module.exports = mongoose.model("Share", Share); \ No newline at end of file diff --git a/tests/api/activity.spec.js b/tests/api/activity.spec.js index 0f3a702..4e9a9a3 100644 --- a/tests/api/activity.spec.js +++ b/tests/api/activity.spec.js @@ -24,8 +24,9 @@ describe("activities", function () { describe("registered and logged in `testuser`", function () { - var cookieJar = request.jar(); + var cookieJar; beforeEach(function (next) { + cookieJar = request.jar(); //register + login request.post({ uri: helpers.apiendpoint+"/users/register", @@ -52,12 +53,15 @@ describe("activities", function () { }); describe("with a publication", function () { + var messageId, authorId; beforeEach(function (next) { request.post({ uri: helpers.apiendpoint + "/activity/share", json: { "body": "A simple text message" }, jar: cookieJar }, function (err, res, body) { + messageId = body.message; + authorId = body.author; next(); }); }); @@ -68,7 +72,6 @@ describe("activities", function () { json: {}, jar: cookieJar }, function(err, res, body){ - console.log("list result:", body); expect(res.statusCode).toBe(200); expect(body.shares.length).toEqual(1); expect(Object.keys(body.messages).length).toEqual(1); @@ -76,6 +79,65 @@ describe("activities", function () { }); }); + describe("with a secondUser", function () { + var secondUserJar, secondUserId; + beforeEach(function (next) { + secondUserJar = request.jar(); + //register + login + request.post({ + uri: helpers.apiendpoint+"/users/register", + json: { + username: "secondUser", + password: "test" + }, + jar: secondUserJar + }, function(err, res, body){ + secondUserId = body.result._id; + next(); + }); + }); + + it("reshares a publication", function (next) { + request.post({ + uri: helpers.apiendpoint + "/activity/reshare", + json: { message: messageId, parent:authorId }, + jar: secondUserJar + }, function (err, res, body) { + expect(err).toBeFalsy(); + expect(body.message).toEqual(messageId); + expect(body.parent).toEqual(authorId); + expect(body.author).toEqual(secondUserId); + next(); + }); + }); + + describe("and a reshare", function () { + beforeEach(function (next) { + request.post({ + uri: helpers.apiendpoint + "/activity/reshare", + json: { message: messageId, author: secondUserId, parent:authorId }, + jar: secondUserJar + }, function (err, res, body) { + next(); + }); + }); + + it("lists both shares", function (next) { + request.get({ + uri: helpers.apiendpoint + "/activity/list", + json: {}, + jar: cookieJar + }, function(err, res, body){ + expect(res.statusCode).toBe(200); + expect(body.shares.length).toEqual(2); + expect(Object.keys(body.messages).length).toEqual(1); + next(); + }); + }); + }); + + }); + }); }); }); \ No newline at end of file