backend for secure categories mostly done (todo pm groups)

This commit is contained in:
Sam 2013-04-29 16:33:24 +10:00
parent a99efecb39
commit 5cfcdc7ef0
16 changed files with 353 additions and 238 deletions

View File

@ -13,6 +13,9 @@ class Category < ActiveRecord::Base
has_many :category_featured_users
has_many :featured_users, through: :category_featured_users, source: :user
has_many :category_groups
has_many :groups, through: :category_groups
validates :user_id, presence: true
validates :name, presence: true, uniqueness: true, length: { in: 1..50 }
validate :uncategorized_validator
@ -28,6 +31,32 @@ class Category < ActiveRecord::Base
delegate :post_template, to: 'self.class'
# Internal: Update category stats: # of topics in past year, month, week for
# all categories.
def self.update_stats
topics = Topic
.select("COUNT(*)")
.where("topics.category_id = categories.id")
.where("categories.topic_id <> topics.id")
.visible
topic_count = topics.to_sql
topics_year = topics.created_since(1.year.ago).to_sql
topics_month = topics.created_since(1.month.ago).to_sql
topics_week = topics.created_since(1.week.ago).to_sql
Category.update_all("topic_count = (#{topic_count}),
topics_year = (#{topics_year}),
topics_month = (#{topics_month}),
topics_week = (#{topics_week})")
end
# Internal: Generate the text of post prompting to enter category
# description.
def self.post_template
I18n.t("category.post_template", replace_paragraph: I18n.t("category.replace_paragraph"))
end
def create_category_definition
create_topic!(title: I18n.t("category.topic_prefix", category: name), user: user, pinned_at: Time.now)
update_column(:topic_id, topic.id)
@ -66,29 +95,23 @@ class Category < ActiveRecord::Base
errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
end
# Internal: Update category stats: # of topics in past year, month, week for
# all categories.
def self.update_stats
topics = Topic
.select("COUNT(*)")
.where("topics.category_id = categories.id")
.where("categories.topic_id <> topics.id")
.visible
topic_count = topics.to_sql
topics_year = topics.created_since(1.year.ago).to_sql
topics_month = topics.created_since(1.month.ago).to_sql
topics_week = topics.created_since(1.week.ago).to_sql
Category.update_all("topic_count = (#{topic_count}),
topics_year = (#{topics_year}),
topics_month = (#{topics_month}),
topics_week = (#{topics_week})")
def secure?
self.secure
end
# Internal: Generate the text of post prompting to enter category
# description.
def self.post_template
I18n.t("category.post_template", replace_paragraph: I18n.t("category.replace_paragraph"))
def deny(group)
if group == :all
self.secure = true
end
end
def allow(group)
if group == :all
self.secure = false
category_groups.clear
else
groups.push(group)
end
end
end

View File

@ -0,0 +1,4 @@
class CategoryGroup < ActiveRecord::Base
belongs_to :category
belongs_to :group
end

View File

@ -1,5 +1,15 @@
class Group < ActiveRecord::Base
has_many :category_groups
has_many :group_users
has_many :categories, through: :category_groups
has_many :users, through: :group_users
def self.builtin
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
end
def add(user)
self.users.push(user)
end
end

View File

@ -1,2 +1,4 @@
class GroupUser < ActiveRecord::Base
belongs_to :group
belongs_to :user
end

View File

@ -68,7 +68,7 @@ class Topic < ActiveRecord::Base
scope :listable_topics, lambda { where('topics.archetype <> ?', [Archetype.private_message]) }
scope :by_newest, order('created_at desc, id desc')
scope :by_newest, order('topics.created_at desc, topics.id desc')
# Helps us limit how many favorites can be made in a day
class FavoriteLimiter < RateLimiter

View File

@ -26,6 +26,10 @@ class User < ActiveRecord::Base
has_one :github_user_info, dependent: :destroy
belongs_to :approved_by, class_name: 'User'
has_many :group_users
has_many :groups, through: :group_users
has_many :secure_categories, through: :groups, source: :categories
validates_presence_of :username
validates_presence_of :email
validates_uniqueness_of :email
@ -150,10 +154,6 @@ class User < ActiveRecord::Base
u.errors[:username].blank?
end
def enqueue_welcome_message(message_type)
return unless SiteSetting.send_welcome_message?
Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type)
end
def self.suggest_name(email)
return "" unless email
@ -162,6 +162,25 @@ class User < ActiveRecord::Base
name.titleize
end
# Find a user by temporary key, nil if not found or key is invalid
def self.find_by_temporary_key(key)
user_id = $redis.get("temporary_key:#{key}")
if user_id.present?
where(id: user_id.to_i).first
end
end
def self.find_by_username_or_email(username_or_email)
lower_user = username_or_email.downcase
lower_email = Email.downcase(username_or_email)
where("username_lower = :user or lower(username) = :user or email = :email or lower(name) = :user", user: lower_user, email: lower_email)
end
def enqueue_welcome_message(message_type)
return unless SiteSetting.send_welcome_message?
Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type)
end
def change_username(new_username)
current_username, self.username = username, new_username
@ -185,19 +204,6 @@ class User < ActiveRecord::Base
key
end
# Find a user by temporary key, nil if not found or key is invalid
def self.find_by_temporary_key(key)
user_id = $redis.get("temporary_key:#{key}")
if user_id.present?
where(id: user_id.to_i).first
end
end
def self.find_by_username_or_email(username_or_email)
lower_user = username_or_email.downcase
lower_email = Email.downcase(username_or_email)
where("username_lower = :user or lower(username) = :user or email = :email or lower(name) = :user", user: lower_user, email: lower_email)
end
# tricky, we need our bus to be subscribed from the right spot
def sync_notification_channel_position
@ -510,6 +516,11 @@ class User < ActiveRecord::Base
.count
end
def secure_category_ids
cats = self.moderator? ? Category.select(:id).where(:secure, true) : secure_categories.select('categories.id')
cats.map{|c| c.id}
end
protected
def cook
@ -591,4 +602,5 @@ class User < ActiveRecord::Base
end
end
end

View File

@ -35,27 +35,28 @@ class UserAction < ActiveRecord::Base
EDIT
].each_with_index.to_a.flatten]
def self.stats(user_id, guardian)
results = UserAction.select("action_type, COUNT(*) count")
.joins(:target_topic)
.where(user_id: user_id)
.group('action_type')
# We apply similar filters in stream, might consider trying to consolidate somehow
unless guardian.can_see_private_messages?(user_id)
results = results.where('topics.archetype <> ?', Archetype::private_message)
end
# Sam: I tried this in AR and it got complex
builder = UserAction.sql_builder <<SQL
unless guardian.user && guardian.user.id == user_id
results = results.where("action_type <> ?", BOOKMARK)
end
SELECT action_type, COUNT(*) count
FROM user_actions a
JOIN topics t ON t.id = a.target_topic_id
LEFT JOIN posts p on p.id = a.target_post_id
JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
LEFT JOIN categories c ON c.id = t.category_id
/*where*/
GROUP BY action_type
SQL
unless guardian.can_see_deleted_posts?
results = results.where('topics.deleted_at IS NULL')
end
results = results.to_a
builder.where('a.user_id = :user_id', user_id: user_id)
apply_common_filters(builder, user_id, guardian)
results = builder.exec.to_a
results.sort! { |a,b| ORDER[a.action_type] <=> ORDER[b.action_type] }
results
@ -93,23 +94,14 @@ JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
JOIN users u on u.id = a.acting_user_id
JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
JOIN users au on au.id = a.user_id
LEFT JOIN categories c on c.id = t.category_id
/*where*/
/*order_by*/
/*offset*/
/*limit*/
")
unless guardian.can_see_deleted_posts?
builder.where("p.deleted_at is null and p2.deleted_at is null and t.deleted_at is null")
end
unless guardian.user && guardian.user.id == user_id
builder.where("a.action_type not in (#{BOOKMARK})")
end
if !guardian.can_see_private_messages?(user_id) || ignore_private_messages
builder.where("t.archetype != :archetype", archetype: Archetype::private_message)
end
apply_common_filters(builder, user_id, guardian, ignore_private_messages)
if action_id
builder.where("a.id = :id", id: action_id.to_i)
@ -182,6 +174,34 @@ JOIN users au on au.id = a.user_id
end
protected
def self.apply_common_filters(builder,user_id,guardian,ignore_private_messages=false)
unless guardian.can_see_deleted_posts?
builder.where("p.deleted_at is null and p2.deleted_at is null and t.deleted_at is null")
end
unless guardian.user && guardian.user.id == user_id
builder.where("a.action_type not in (#{BOOKMARK})")
end
if !guardian.can_see_private_messages?(user_id) || ignore_private_messages
builder.where("t.archetype != :archetype", archetype: Archetype::private_message)
end
unless guardian.is_moderator?
allowed = guardian.secure_category_ids
if allowed.present?
builder.where("( c.secure IS NULL OR
c.secure = 'f' OR
(c.secure = 't' and c.id in (:cats)) )", cats: guardian.secure_category_ids )
else
builder.where("(c.secure IS NULL OR c.secure = 'f')")
end
end
end
def self.require_parameters(data, *params)
params.each do |p|
raise Discourse::InvalidParameters.new(p) if data[p].nil?

View File

@ -0,0 +1,11 @@
class AddSecurityToCategories < ActiveRecord::Migration
def change
add_column :categories, :secure, :boolean, default: false, null: false
create_table :category_groups do |t|
t.integer :category_id, null: false
t.integer :group_id, null: false
t.timestamps
end
end
end

View File

@ -12,7 +12,11 @@ class Guardian
end
def is_admin?
!@user.nil? && @user.admin?
@user && @user.admin?
end
def is_moderator?
@user && @user.moderator?
end
# Can the user see the object?
@ -329,6 +333,15 @@ class Guardian
end
def can_see_topic?(topic)
return false unless topic
return true if @user && @user.moderator?
return false if topic.deleted_at.present?
if topic.category && topic.category.secure
return false unless @user && can_see_category?(topic.category)
end
if topic.private_message?
return false if @user.blank?
return true if topic.allowed_users.include?(@user)
@ -337,6 +350,22 @@ class Guardian
true
end
def can_see_post?(post)
return false unless post
return true if @user && @user.moderator?
return false if post.deleted_at.present?
can_see_topic?(post.topic)
end
def can_see_category?(category)
return true unless category.secure
return false unless @user
@user.secure_category_ids.include?(category.id)
end
def can_vote?(post, opts={})
post_can_act?(post,:vote, opts)
end
@ -371,4 +400,7 @@ class Guardian
return true
end
def secure_category_ids
@user ? @user.secure_category_ids : []
end
end

View File

@ -207,13 +207,13 @@ class TopicQuery
# Start with a list of all topics
result = Topic
if @user_id.present?
if @user_id
result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
end
unless query_opts[:unordered]
# If we're logged in, we have to pay attention to our pinned settings
if @user_id.present?
if @user
result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
else
result = result.order(TopicQuery.order_nocategory_basic_bumped)
@ -227,6 +227,16 @@ class TopicQuery
result = result.visible if @user.blank? or @user.regular?
result = result.where('topics.id <> ?', query_opts[:except_topic_id]) if query_opts[:except_topic_id].present?
result = result.offset(query_opts[:page].to_i * page_size) if query_opts[:page].present?
unless @user && @user.moderator?
category_ids = @user.secure_category_ids if @user
if category_ids.present?
result = result.where('categories.secure IS NULL OR categories.secure = ? OR categories.id IN (?)', false, category_ids)
else
result = result.where('categories.secure IS NULL OR categories.secure = ?', false)
end
end
result
end

View File

@ -144,28 +144,14 @@ describe Guardian do
let(:topic) { Fabricate(:topic, user: coding_horror)}
it 'returns false when the post is nil' do
Guardian.new(user).can_see_post_actors?(nil, PostActionType.types[:like]).should be_false
end
it 'returns true for likes' do
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:like]).should be_true
end
it 'returns false for bookmarks' do
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:bookmark]).should be_false
end
it 'returns false for off-topic flags' do
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:off_topic]).should be_false
end
it 'returns false for spam flags' do
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:spam]).should be_false
end
it 'returns true for public votes' do
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:vote]).should be_true
it 'displays visibility correctly' do
guardian = Guardian.new(user)
guardian.can_see_post_actors?(nil, PostActionType.types[:like]).should be_false
guardian.can_see_post_actors?(topic, PostActionType.types[:like]).should be_true
guardian.can_see_post_actors?(topic, PostActionType.types[:bookmark]).should be_false
guardian.can_see_post_actors?(topic, PostActionType.types[:off_topic]).should be_false
guardian.can_see_post_actors?(topic, PostActionType.types[:spam]).should be_false
guardian.can_see_post_actors?(topic, PostActionType.types[:vote]).should be_true
end
it 'returns false for private votes' do
@ -176,34 +162,15 @@ describe Guardian do
end
describe 'can_impersonate?' do
it 'returns false when the target is nil' do
it 'allows impersonation correctly' do
Guardian.new(admin).can_impersonate?(nil).should be_false
end
it 'returns false when the user is nil' do
Guardian.new.can_impersonate?(user).should be_false
end
it "doesn't allow a non-admin to impersonate someone" do
Guardian.new(coding_horror).can_impersonate?(user).should be_false
end
it "doesn't allow an admin to impersonate themselves" do
Guardian.new(admin).can_impersonate?(admin).should be_false
end
it "doesn't allow an admin to impersonate another admin" do
Guardian.new(admin).can_impersonate?(another_admin).should be_false
end
it "allows an admin to impersonate a regular user" do
Guardian.new(admin).can_impersonate?(user).should be_true
end
it "allows an admin to impersonate a moderator" do
Guardian.new(admin).can_impersonate?(moderator).should be_true
end
end
describe 'can_invite_to?' do
@ -211,16 +178,11 @@ describe Guardian do
let(:user) { topic.user }
let(:moderator) { Fabricate(:moderator) }
it 'returns false with a nil user' do
it 'handles invitation correctly' do
Guardian.new(nil).can_invite_to?(topic).should be_false
end
it 'returns false with a nil object' do
Guardian.new(moderator).can_invite_to?(nil).should be_false
end
it 'returns true for a moderator to invite' do
Guardian.new(moderator).can_invite_to?(topic).should be_true
Guardian.new(user).can_invite_to?(topic).should be_false
end
it 'returns false when the site requires approving users' do
@ -228,10 +190,6 @@ describe Guardian do
Guardian.new(moderator).can_invite_to?(topic).should be_false
end
it 'returns false for a regular user to invite' do
Guardian.new(user).can_invite_to?(topic).should be_false
end
end
describe 'can_see?' do
@ -244,6 +202,40 @@ describe Guardian do
it 'allows non logged in users to view topics' do
Guardian.new.can_see?(topic).should be_true
end
it 'correctly handles groups' do
group = Fabricate(:group)
category = Fabricate(:category, secure: true)
category.allow(group)
topic = Fabricate(:topic, category: category)
Guardian.new(user).can_see?(topic).should be_false
group.add(user)
group.save
Guardian.new(user).can_see?(topic).should be_true
end
end
describe 'a Post' do
it 'correctly handles post visibility' do
Guardian.new(user).can_see?(post).should be_true
post.destroy
post.reload
Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true
post.recover
post.reload
topic.destroy
topic.reload
Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true
end
end
end

View File

@ -3,13 +3,38 @@ require 'topic_view'
describe TopicQuery do
let!(:user) { Fabricate(:coding_horror) }
let(:user) { Fabricate(:coding_horror) }
let(:creator) { Fabricate(:user) }
let(:topic_query) { TopicQuery.new(user) }
let(:moderator) { Fabricate(:moderator) }
let(:admin) { Fabricate(:moderator) }
context 'secure category' do
it "filters categories out correctly" do
category = Fabricate(:category)
category.deny(:all)
group = Fabricate(:group)
category.allow(group)
category.save
topic = Fabricate(:topic, category: category)
TopicQuery.new(nil).list_latest.topics.count.should == 0
TopicQuery.new(user).list_latest.topics.count.should == 0
# mods can see every group
TopicQuery.new(moderator).list_latest.topics.count.should == 2
group.add(user)
group.save
TopicQuery.new(user).list_latest.topics.count.should == 2
end
end
context 'a bunch of topics' do
let!(:regular_topic) { Fabricate(:topic, title: 'this is a regular topic', user: creator, bumped_at: 15.minutes.ago) }
let!(:pinned_topic) { Fabricate(:topic, title: 'this is a pinned topic', user: creator, pinned_at: 10.minutes.ago, bumped_at: 10.minutes.ago) }
@ -90,17 +115,11 @@ describe TopicQuery do
context 'with no data' do
it "has no read topics" do
topic_query.list_unread.topics.should be_blank
end
it "has no unread topics" do
topic_query.list_unread.topics.should be_blank
end
it "has an unread count of 0" do
topic_query.unread_count.should == 0
end
end
context 'with read data' do
@ -115,9 +134,6 @@ describe TopicQuery do
context 'list_unread' do
it 'contains no topics' do
topic_query.list_unread.topics.should == []
end
it "returns 0 as the unread count" do
topic_query.unread_count.should == 0
end
end

View File

@ -0,0 +1,3 @@
Fabricator(:group) do
name 'my group'
end

View File

@ -18,6 +18,34 @@ describe Category do
it { should have_many :category_featured_topics }
it { should have_many :featured_topics }
describe "security" do
it "secures categories correctly" do
category = Fabricate(:category)
category.secure?.should be_false
category.deny(:all)
category.secure?.should be_true
category.allow(:all)
category.secure?.should be_false
user = Fabricate(:user)
user.secure_categories.to_a.should == []
group = Fabricate(:group)
group.add(user)
group.save
category.allow(group)
category.save
user.reload
user.secure_categories.to_a.should == [category]
end
end
describe "uncategorized name" do
let(:category) { Fabricate.build(:category, name: SiteSetting.uncategorized_name) }
@ -71,47 +99,27 @@ describe Category do
@topic = @category.topic
end
it 'creates a slug' do
it 'is created correctly' do
@category.slug.should == 'amazing-category'
end
it "has a hotness of 5.0 by default" do
@category.hotness.should == 5.0
end
it 'has a default description' do
@category.description.should be_blank
end
it 'has one topic' do
Topic.where(category_id: @category).count.should == 1
end
it 'creates a topic post' do
@topic.should be_present
end
it 'points back to itself' do
@topic.category.should == @category
end
it 'is a visible topic' do
@topic.should be_visible
end
it 'is pinned' do
@topic.pinned_at.should be_present
end
it 'is an undeletable topic' do
Guardian.new(@category.user).can_delete?(@topic).should be_false
end
it 'should have one post' do
@topic.posts.count.should == 1
end
it 'should have a topic url' do
@category.topic_url.should be_present
end
@ -129,17 +137,12 @@ describe Category do
@category.reload
end
it 'still has 0 forum topics' do
it 'does not cause changes' do
@category.topic_count.should == 0
end
it "didn't change the category" do
@topic.category.should == @category
end
it "didn't change the category's forum topic" do
@category.topic.should == @topic
end
end
end
@ -151,11 +154,8 @@ describe Category do
@category.destroy
end
it 'deletes the category' do
it 'is deleted correctly' do
Category.exists?(id: @category_id).should be_false
end
it 'deletes the forum topic' do
Topic.exists?(id: @topic_id).should be_false
end
end
@ -172,21 +172,13 @@ describe Category do
@category.reload
end
it 'updates topics_week' do
it 'updates topic stats' do
@category.topics_week.should == 1
end
it 'updates topics_month' do
@category.topics_month.should == 1
end
it 'updates topics_year' do
@category.topics_year.should == 1
end
it 'updates topic_count' do
@category.topic_count.should == 1
end
end
context 'with deleted topics' do
@ -197,21 +189,13 @@ describe Category do
@category.reload
end
it 'does not count deleted topics for topics_week' do
it 'does not count deleted topics' do
@category.topics_week.should == 0
end
it 'does not count deleted topics for topics_month' do
@category.topic_count.should == 0
@category.topics_month.should == 0
end
it 'does not count deleted topics for topics_year' do
@category.topics_year.should == 0
end
it 'does not count deleted topics for topic_count' do
@category.topic_count.should == 0
end
end
end
end

View File

@ -0,0 +1,4 @@
require 'spec_helper'
describe Group do
end

View File

@ -37,41 +37,52 @@ describe UserAction do
log_test_action(action_type: UserAction::BOOKMARK)
end
describe 'stats' do
let :mystats do
UserAction.stats(user.id, Guardian.new(user))
end
it 'include correct events' do
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::NEW_TOPIC)
UserAction.stats(user.id,Guardian.new).map{|r| r["action_type"].to_i}.should_not include(UserAction::NEW_PRIVATE_MESSAGE)
UserAction.stats(user.id,Guardian.new).map{|r| r["action_type"].to_i}.should_not include(UserAction::GOT_PRIVATE_MESSAGE)
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::NEW_PRIVATE_MESSAGE)
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::GOT_PRIVATE_MESSAGE)
end
it 'should not include new topic when topic is deleted' do
public_topic.destroy
mystats.map{|r| r["action_type"].to_i}.should_not include(UserAction::NEW_TOPIC)
end
def stats_for_user(viewer=nil)
UserAction.stats(user.id, Guardian.new(viewer)).map{|r| r["action_type"].to_i}.sort
end
describe 'stream' do
def stream_count(viewer=nil)
UserAction.stream(user_id: user.id, guardian: Guardian.new(viewer)).count
end
it 'should have 1 item for non owners' do
UserAction.stream(user_id: user.id, guardian: Guardian.new).count.should == 1
end
it 'includes the events correctly' do
it 'should include no items for non owner when topic deleted' do
public_topic.destroy
UserAction.stream(user_id: user.id, guardian: Guardian.new).count.should == 0
end
mystats = stats_for_user(user)
expecting = [UserAction::NEW_TOPIC, UserAction::NEW_PRIVATE_MESSAGE, UserAction::GOT_PRIVATE_MESSAGE, UserAction::BOOKMARK].sort
mystats.should == expecting
stream_count(user).should == 4
it 'should have bookmarks and pms for owners' do
UserAction.stream(user_id: user.id, guardian: user.guardian).count.should == 4
end
other_stats = stats_for_user
expecting = [UserAction::NEW_TOPIC]
stream_count.should == 1
other_stats.should == expecting
public_topic.destroy
stats_for_user.should == []
stream_count.should == 0
# groups
category = Fabricate(:category, secure: true)
public_topic.recover
public_topic.category = category
public_topic.save
stats_for_user.should == []
stream_count.should == 0
group = Fabricate(:group)
u = Fabricate(:coding_horror)
group.add(u)
group.save
category.allow(group)
category.save
stats_for_user(u).should == [UserAction::NEW_TOPIC]
stream_count(u).should == 1
end
end
@ -146,8 +157,6 @@ describe UserAction do
end
it 'should exist' do
@action.should_not be_nil
end
it 'shoule have the correct date' do
@action.created_at.should be_within(1).of(@post.topic.created_at)
end
end
@ -164,16 +173,11 @@ describe UserAction do
@response = Fabricate(:post, reply_to_post_number: 1, topic: @post.topic, user: @other_user, raw: "perhaps @#{@mentioned.username} knows how this works?")
end
it 'should log a post action for the poster' do
it 'should log user actions correctly' do
@response.user.user_actions.where(action_type: UserAction::POST).first.should_not be_nil
end
it 'should log a post action for the original poster' do
@post.user.user_actions.where(action_type: UserAction::RESPONSE).first.should_not be_nil
end
it 'should log a mention for the mentioned' do
@mentioned.user_actions.where(action_type: UserAction::MENTION).first.should_not be_nil
@post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count.should == 1
end
it 'should not log a double notification for a post edit' do
@ -182,12 +186,7 @@ describe UserAction do
@response.user.user_actions.where(action_type: UserAction::POST).count.should == 1
end
it 'should not log topic reply and reply for a single post' do
@post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count.should == 1
end
end
end
describe 'when user bookmarks' do
@ -198,19 +197,12 @@ describe UserAction do
@action = @user.user_actions.where(action_type: UserAction::BOOKMARK).first
end
it 'should create a bookmark action' do
it 'should create a bookmark action correctly' do
@action.action_type.should == UserAction::BOOKMARK
end
it 'should point to the correct post' do
@action.target_post_id.should == @post.id
end
it 'should have the right acting_user' do
@action.acting_user_id.should == @user.id
end
it 'should target the correct user' do
@action.user_id.should == @user.id
end
it 'should nuke the action when unbookmarked' do
PostAction.remove_act(@user, @post, PostActionType.types[:bookmark])
@user.user_actions.where(action_type: UserAction::BOOKMARK).first.should be_nil
end