diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 143ddc880..fc650a4b3 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -113,7 +113,7 @@ class BuildListsController < ApplicationController @build_list.notified_at = Time.current @build_list.save - @build_list.publish if @build_list.auto_publish # && @build_list.can_publish? + @build_list.delay.publish if @build_list.auto_publish # && @build_list.can_publish? render :nothing => true, :status => 200 end diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb new file mode 100644 index 000000000..e9ff43abf --- /dev/null +++ b/app/controllers/subscribes_controller.rb @@ -0,0 +1,25 @@ +class SubscribesController < ApplicationController + before_filter :authenticate_user! + + load_and_authorize_resource :project + load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id + load_and_authorize_resource :subscribe, :through => :issue, :find_by => :user_id + + def create + @subscribe = @issue.subscribes.build(:user_id => current_user.id) + if @subscribe.save + flash[:notice] = I18n.t("flash.subscribe.saved") + redirect_to :back + else + flash[:error] = I18n.t("flash.subscribe.saved_error") + redirect_to :back + end + end + + def destroy + @subscribe.destroy + + flash[:notice] = t("flash.subscribe.destroyed") + redirect_to :back + end +end diff --git a/app/helpers/subscribes_helper.rb b/app/helpers/subscribes_helper.rb new file mode 100644 index 000000000..dc1b490ec --- /dev/null +++ b/app/helpers/subscribes_helper.rb @@ -0,0 +1,2 @@ +module SubscribesHelper +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 3137b0dff..2c43482e1 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -5,7 +5,31 @@ class UserMailer < ActionMailer::Base def new_user_notification(user) @user = user - mail(:to => user.email, :subject => "Регистрация на проекте «#{APP_CONFIG['project_name']}»") do |format| + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_user_notification", :project_name => APP_CONFIG['project_name'])) do |format| + format.html + end + end + + def new_comment_notification(comment, user) + @user = user + @comment = comment + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_comment_notification")) do |format| + format.html + end + end + + def new_issue_notification(issue, user) + @user = user + @issue = issue + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_issue_notification")) do |format| + format.html + end + end + + def issue_assign_notification(issue, user) + @user = user + @issue = issue + mail(:to => user.email, :subject => I18n.t("notifications.subjects.issue_assign_notification")) do |format| format.html end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9a601f37e..1b7509a2b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -13,6 +13,8 @@ class Ability if user.admin? can :manage, :all + cannot :destroy, Subscribe + cannot :create, Subscribe else # Shared rights between guests and registered users can :forbidden, Platform @@ -88,7 +90,7 @@ class Ability cannot :manage, Issue, :project => {:has_issues => false} # switch off issues can(:create, Comment) {|comment| can? :read, comment.commentable.project} - can(:update, Comment) {|comment| can? :update, comment.user_id == user.id or local_admin?(comment.commentable.project)} + can(:update, Comment) {|comment| comment.user_id == user.id or local_admin?(comment.commentable.project)} cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues end end @@ -98,6 +100,13 @@ class Ability cannot :destroy, Repository, :platform => {:platform_type => 'personal'} cannot :fork, Project, :owner_id => user.id, :owner_type => user.class.to_s cannot :destroy, Issue + + can :create, Subscribe do |subscribe| + !subscribe.subscribeable.subscribes.exists?(:user_id => user.id) + end + can :destroy, Subscribe do |subscribe| + subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id + end end # TODO group_ids ?? diff --git a/app/models/comment.rb b/app/models/comment.rb index 4c15a8b66..439fd0733 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -3,4 +3,18 @@ class Comment < ActiveRecord::Base belongs_to :user validates :body, :user_id, :commentable_id, :commentable_type, :presence => true + + after_create :deliver_new_comment_notification + + protected + + def deliver_new_comment_notification + recipients = self.commentable.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } + recipients = recipients | [self.commentable.user_id] + recipients = recipients | [self.commentable.project.owner_id] if self.commentable.project.owner_type == 'User' + recipients.each do |recipient_id| + recipient = User.find(recipient_id) + UserMailer.delay.new_comment_notification(self, recipient) + end + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index f320f3309..9ae96f439 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -5,12 +5,21 @@ class Issue < ActiveRecord::Base belongs_to :user has_many :comments, :as => :commentable + has_many :subscribes, :as => :subscribeable - validates :title, :body, :project_id, :user_id, :presence => true + validates :title, :body, :project_id, :presence => true #attr_readonly :serial_id after_create :set_serial_id + after_create :subscribe_users + after_create :deliver_new_issue_notification + after_create :deliver_issue_assign_notification + after_update :deliver_issue_assign_notification + + def assign_uname + user.uname if user + end def to_param serial_id.to_s @@ -22,4 +31,32 @@ class Issue < ActiveRecord::Base self.serial_id = self.project.issues.count self.save! end + + def deliver_new_issue_notification + recipients = collect_recipient_ids + recipients.each do |recipient_id| + recipient = User.find(recipient_id) + UserMailer.delay.new_issue_notification(self, recipient)#.deliver + end + end + + def deliver_issue_assign_notification + UserMailer.delay.issue_assign_notification(self, self.user) if self.user_id_was != self.user_id + end + + def subscribe_users + recipients = collect_recipient_ids + recipients.each do |recipient_id| + ss = self.subscribes.build(:user_id => recipient_id) + ss.save! + end + end + + def collect_recipient_ids + recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } + recipients = recipients | [self.user_id] if self.user_id + recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User' + recipients + end + end diff --git a/app/models/relation.rb b/app/models/relation.rb index 822687dca..a08ddf6a9 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -10,6 +10,7 @@ class Relation < ActiveRecord::Base scope :by_object, lambda {|obj| {:conditions => ['object_id = ? AND object_type = ?', obj.id, obj.class.to_s]}} scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}} + scope :by_role, lambda {|role| {:conditions => ['role = ?', role]}} def self.create_with_role(object, target, role) r = new diff --git a/app/models/subscribe.rb b/app/models/subscribe.rb new file mode 100644 index 000000000..0c3a52698 --- /dev/null +++ b/app/models/subscribe.rb @@ -0,0 +1,4 @@ +class Subscribe < ActiveRecord::Base + belongs_to :subscribeable, :polymorphic => true + belongs_to :user +end diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index eb5acaf4d..4078f495e 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml @@ -1,5 +1,5 @@ .group - = f.label :body, :class => :label + = f.label :body, t("activerecord.attributes.comment.body"), :class => :label = f.text_area :body, :class => 'text_field', :cols => 80 .group.navform.wat-cf diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index bb1bc72ed..985ff7913 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -8,12 +8,13 @@ = f.label :body, :class => :label = f.text_area :body, :class => 'text_field', :cols => 80 -.group - = f.label :status, :class => :label - = f.select :status, Issue::STATUSES, :class => 'text_field' +- unless @issue.new_record? + .group + = f.label :status, :class => :label + = f.select :status, Issue::STATUSES, :class => 'text_field' .group - = label_tag "", t("layout.issues.user_id"), :class => :label + = label_tag "", t("activerecord.attributes.issue.user_id"), :class => :label = autocomplete_field_tag 'user_id', @user_uname, autocomplete_user_uname_platforms_path, :id_element => '#user_id_field' = hidden_field_tag 'user_id', @user_id, :id => 'user_id_field' diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index 98d5d5448..096558d58 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -1,12 +1,15 @@ %table.table %tr %th.first= t("activerecord.attributes.issue.title") + %th.first= t("activerecord.attributes.issue.user") %th.first= t("activerecord.attributes.issue.status") %th.last   - @issues.each do |issue| %tr{:class => cycle("odd", "even")} %td = link_to issue.title, [@project, issue] + %td + = link_to issue.user.uname, user_path(issue.user) if issue.user %td = issue.status %td.last diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index a68e58fea..49b229b5c 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -20,29 +20,32 @@ = t('activerecord.attributes.issue.status') \: = @issue.status + %p + %b + = t('layout.issues.subscribe') + \: + - if @issue.subscribes.exists? :user_id => current_user.id + = link_to t('layout.issues.unsubscribe_btn'), project_issue_subscribe_path(@project, @issue, current_user.id), :method => :delete + - else + = link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post %a{ :name => "comments" } -.block - .secondary-navigation - %ul.wat-cf +.block#block-list .content %h2.title = t("layout.issues.comments_header") .inner - %table.table - %tr - %th.first= t("activerecord.attributes.user.uname") - %th.first= t("activerecord.attributes.comment.body") - %th.last   + %ul.list - @issue.comments.each do |comment| - %tr{:class => cycle("odd", "even")} - %td - = comment.user.uname - %td + %li + .left + = link_to comment.user.uname, user_path(comment.user.uname) + .item = comment.body - %td.last - = link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue, comment) if can? :update, comment - = link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment + %br + %br + = link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue.id, comment) if can? :update, comment + = link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue.id, comment), :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment .block .content diff --git a/app/views/layouts/_stylesheets.html.haml b/app/views/layouts/_stylesheets.html.haml index b74e60ab4..101eb8857 100644 --- a/app/views/layouts/_stylesheets.html.haml +++ b/app/views/layouts/_stylesheets.html.haml @@ -1,6 +1,6 @@ -#= include_stylesheets :application = stylesheet_link_tag "web-app-theme/base.css", "web-app-theme/themes/default/style.css", "web-app-theme/override.css", "git/style.css" -= stylesheet_link_tag "jquery-ui-1.8.16.custom.css", "datatable.css" += stylesheet_link_tag "jquery-ui-1.8.16.custom.css", "datatable.css", "patches.css" = yield :stylesheets diff --git a/app/views/user_mailer/issue_assign_notification.haml b/app/views/user_mailer/issue_assign_notification.haml new file mode 100644 index 000000000..a6615d3eb --- /dev/null +++ b/app/views/user_mailer/issue_assign_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p Вам была назначена задача #{ link_to @issue.title, [@issue.project, @issue] } + + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_comment_notification.haml b/app/views/user_mailer/new_comment_notification.haml new file mode 100644 index 000000000..88692f64e --- /dev/null +++ b/app/views/user_mailer/new_comment_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p К задаче #{ link_to @comment.commentable.title, [@comment.commentable.project, @comment.commentable] } был добавлен новый комментарий. + + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_issue_notification.haml b/app/views/user_mailer/new_issue_notification.haml new file mode 100644 index 000000000..3a2604cfb --- /dev/null +++ b/app/views/user_mailer/new_issue_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p К проекту #{ link_to @issue.project.name, project_path(@issue.project) } была добавлена задача #{ link_to @issue.title, [@issue.project, @issue] } + + +%p== Команда поддержки «ROSA Build System» diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 7fcd2b6c0..b55ffdc0e 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -112,11 +112,15 @@ ru: new: Новая задача list_header: Список confirm_delete: Вы уверены, что хотите удалить эту задачу? + edit_header: Редактирование задачи new_header: Новая задача statuses: open: Открытые closed: Закрытые any: Все + subscribe: Подписка на уведомления + subscribe_btn: Подписаться + unsubscribe_btn: Отписаться comments: confirm_delete: Вы уверены, что хотите удалить комментарий? @@ -360,6 +364,10 @@ ru: project_version_not_found: версия не найден flash: + subscribe: + saved: Вы подписаны на оповещения для этой задачи + destroyed: Подписка на оповещения для этой задачи убрана + exception_message: У Вас нет доступа к этой странице! downloads: @@ -500,7 +508,8 @@ ru: issue: title: Заголовок body: Содержание - user: Назначено + user: Назначена + user_id: Назначена project: Проект status: Статус @@ -664,3 +673,9 @@ ru: distro: Дистрибутив platform: Архитектура counter: Закачки + notifications: + subjects: + new_comment_notification: Новый комментарий к Вашей задаче + new_issue_notification: Новая задача добавлена к проекту + new_user_notification: Регистрация на проекте «%{ project_name }» + issue_assign_notification: Вам назначили задачу diff --git a/config/routes.rb b/config/routes.rb index ca9648c86..d8e49aa56 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -81,6 +81,7 @@ Rosa::Application.routes.draw do resources :projects do resources :issues do resources :comments, :only => [:edit, :create, :update, :destroy] + resources :subscribes, :only => [:create, :destroy] end resource :repo, :controller => "git/repositories", :only => [:show] resources :build_lists, :only => [:index, :new, :create] diff --git a/db/migrate/20111226141947_create_subscribes.rb b/db/migrate/20111226141947_create_subscribes.rb new file mode 100644 index 000000000..631e09f09 --- /dev/null +++ b/db/migrate/20111226141947_create_subscribes.rb @@ -0,0 +1,14 @@ +class CreateSubscribes < ActiveRecord::Migration + def self.up + create_table :subscribes do |t| + t.integer :subscribeable_id + t.string :subscribeable_type + t.integer :user_id + t.timestamps + end + end + + def self.down + drop_table :subscribes + end +end diff --git a/db/schema.rb b/db/schema.rb index 4fad3e5fa..e80b0c134 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -272,6 +272,14 @@ ActiveRecord::Schema.define(:version => 20111228182425) do add_index "rpms", ["project_id", "arch_id"], :name => "index_rpms_on_project_id_and_arch_id" add_index "rpms", ["project_id"], :name => "index_rpms_on_project_id" + create_table "subscribes", :force => true do |t| + t.integer "subscribeable_id" + t.string "subscribeable_type" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "users", :force => true do |t| t.string "name" t.string "email", :default => "", :null => false diff --git a/public/stylesheets/patches.css b/public/stylesheets/patches.css new file mode 100644 index 000000000..d6a3f9473 --- /dev/null +++ b/public/stylesheets/patches.css @@ -0,0 +1,4 @@ +ul.list li .item { + margin-left: 180px; +} + diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 562bf1aff..c93ac57f0 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -1,5 +1,148 @@ require 'spec_helper' -describe CommentsController do +shared_examples_for 'user with create comment rights' do + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_issue_path(@project, @issue)) + end + it 'should create subscribe object into db' do + lambda{ post :create, @create_params }.should change{ Comment.count }.by(1) + end +end + +shared_examples_for 'user with update own comment rights' do + it 'should be able to perform update action' do + put :update, {:id => @own_comment.id}.merge(@update_params) + response.should redirect_to([@project, @issue]) + end + + it 'should update subscribe body' do + put :update, {:id => @own_comment.id}.merge(@update_params) + @own_comment.reload.body.should == 'updated' + end +end + +shared_examples_for 'user with update stranger comment rights' do + it 'should be able to perform update action' do + put :update, {:id => @comment.id}.merge(@update_params) + response.should redirect_to([@project, @issue]) + end + + it 'should update issue title' do + put :update, {:id => @comment.id}.merge(@update_params) + @comment.reload.body.should == 'updated' + end +end + +shared_examples_for 'user without update stranger comment rights' do + it 'should not be able to perform update action' do + put :update, {:id => @comment.id}.merge(@update_params) + response.should redirect_to(forbidden_path) + end + + it 'should not update issue title' do + put :update, {:id => @comment.id}.merge(@update_params) + @comment.reload.body.should_not == 'updated' + end +end + +shared_examples_for 'user without destroy comment rights' do + it 'should not be able to perform destroy action' do + delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id + response.should redirect_to(forbidden_path) + end + + it 'should not reduce comments count' do + lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(0) + end +end + +#shared_examples_for 'user with destroy rights' do +# it 'should be able to perform destroy action' do +# delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id +# response.should redirect_to([@project, @issue]) +# end +# +# it 'should reduce comments count' do +# lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Comment.count }.by(-1) +# end +#end + +describe CommentsController do + before(:each) do + stub_rsync_methods + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + @comment = Factory(:comment, :commentable => @issue) + + @create_params = {:comment => {:body => 'I am a comment!'}, :project_id => @project.id, :issue_id => @issue.id} + @update_params = {:comment => {:body => 'updated'}, :project_id => @project.id, :issue_id => @issue.id} + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @request.env['HTTP_REFERER'] = project_issue_path(@project, @issue) + end + + context 'for project admin user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user with update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + end + + context 'for project owner user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.update_attribute(:owner, @user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user with update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + end + + context 'for project reader user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'reader') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user without update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + end + + context 'for project writer user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user without update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + end end diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb index 37aad58cf..829957dee 100644 --- a/spec/controllers/issues_controller_spec.rb +++ b/spec/controllers/issues_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' shared_examples_for 'issue user with project reader rights' do - #it_should_behave_like 'user with rights to view issues' it 'should be able to perform index action' do get :index, :project_id => @project.id response.should render_template(:index) @@ -62,7 +61,6 @@ end shared_examples_for 'project with issues turned off' do pending 'should not be able to perform index action' do get :index, :project_id => @project_with_turned_off_issues.id - #response.should redirect_to(forbidden_path) response.should render_template(:index) end @@ -78,8 +76,6 @@ describe IssuesController do @project = Factory(:project) @issue_user = Factory(:user) - @create_params = {:project => {:name => 'pro'}} - @update_params = {:project => {:name => 'pro2'}} any_instance_of(Project, :versions => ['v1.0', 'v2.0']) @@ -116,8 +112,6 @@ describe IssuesController do context 'for project admin user' do before(:each) do - #@admin = Factory(:admin) - #set_session_for(@admin) @user = Factory(:user) set_session_for(@user) @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') @@ -184,7 +178,6 @@ describe IssuesController do context 'for issue assign user' do before(:each) do set_session_for(@issue_user) - #@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer') end it_should_behave_like 'user with issue update rights' diff --git a/spec/controllers/subscribes_controller_spec.rb b/spec/controllers/subscribes_controller_spec.rb new file mode 100644 index 000000000..6db7e2c24 --- /dev/null +++ b/spec/controllers/subscribes_controller_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +shared_examples_for 'can subscribe' do + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_issue_path(@project, @issue)) + end + + it 'should create subscribe object into db' do + lambda{ post :create, @create_params }.should change{ Subscribe.count }.by(1) + end +end + +shared_examples_for 'can not subscribe' do + it 'should not be able to perform create action' do + post :create, @create_params + response.should redirect_to(forbidden_path) + end + + it 'should not create subscribe object into db' do + lambda{ post :create, @create_params }.should change{ Subscribe.count }.by(0) + end +end + +shared_examples_for 'can unsubscribe' do + it 'should be able to perform destroy action' do + delete :destroy, @destroy_params + + response.should redirect_to([@project, @issue]) + end + + it 'should reduce subscribes count' do + lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(-1) + end +end + +shared_examples_for 'can not unsubscribe' do + it 'should not be able to perform destroy action' do + delete :destroy, @destroy_params + + response.should redirect_to(forbidden_path) + end + + it 'should not reduce subscribes count' do + lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(0) + end +end + +describe SubscribesController do + before(:each) do + stub_rsync_methods + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + + @create_params = {:issue_id => @issue.serial_id, :project_id => @project.id} + @destroy_params = {:issue_id => @issue.serial_id, :project_id => @project.id} + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @request.env['HTTP_REFERER'] = project_issue_path(@project, @issue) + end + + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + @destroy_params = @destroy_params.merge({:id => @user.id}) + end + + context 'subscribed' do + before(:each) do + ss = @issue.subscribes.build(:user => @user) + ss.save! + end + + it_should_behave_like 'can unsubscribe' + it_should_behave_like 'can not subscribe' + end + + context 'not subscribed' do + it_should_behave_like 'can subscribe' + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @destroy_params = @destroy_params.merge({:id => @user.id}) + end + + context 'subscribed' do + before(:each) do + ss = @issue.subscribes.build(:user => @user) + ss.save! + end + + it_should_behave_like 'can unsubscribe' + it_should_behave_like 'can not subscribe' + end + + context 'not subscribed' do + it_should_behave_like 'can subscribe' + end + end + +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb index f9df3b695..f036eee06 100644 --- a/spec/factories/comments.rb +++ b/spec/factories/comments.rb @@ -1,6 +1,5 @@ -# Read about factories at http://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :comment do - end -end \ No newline at end of file +Factory.define(:comment) do |p| + p.body { Factory.next(:string) } + p.association :user, :factory => :user + p.association :commentable, :factory => :issue +end diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 8b8017dbc..79d2ca96a 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -1,6 +1,7 @@ Factory.define(:issue) do |p| p.title { Factory.next(:string) } p.body { Factory.next(:string) } + p.association :project, :factory => :project p.association :user, :factory => :user p.status "open" end diff --git a/spec/factories/subscribes.rb b/spec/factories/subscribes.rb new file mode 100644 index 000000000..55a75a8ee --- /dev/null +++ b/spec/factories/subscribes.rb @@ -0,0 +1,4 @@ +Factory.define(:subscribe) do |p| + p.association :subscribeable, :factory => :issue + p.association :user, :factory => :user +end diff --git a/spec/helpers/comments_helper_spec.rb b/spec/helpers/comments_helper_spec.rb deleted file mode 100644 index cc93aa9b6..000000000 --- a/spec/helpers/comments_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the CommentsHelper. For example: -# -# describe CommentsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe CommentsHelper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb deleted file mode 100644 index 2e20f1cf1..000000000 --- a/spec/helpers/issues_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the IssuesHelper. For example: -# -# describe IssuesHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe IssuesHelper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 505e33d36..0b969c134 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -1,5 +1,125 @@ require 'spec_helper' +require "cancan/matchers" + +def set_testable_data + @ability = Ability.new(@user) + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + + @comment = Factory(:comment, :commentable => @issue, :user => @user) + @stranger_comment = Factory(:comment, :commentable => @issue, :user => @stranger) + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) +end describe Comment do - pending "add some examples to (or delete) #{__FILE__}" + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should destroy own comment' do + @ability.should be_able_to(:destroy, @comment) + end + + it 'should destroy stranger comment' do + @ability.should be_able_to(:destroy, @stranger_comment) + end + end + + context 'for project admin user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end + + context 'for project owner user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + + @project.update_attribute(:owner, @user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should not update stranger comment' do + @ability.should_not be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end end diff --git a/spec/models/subscribe_spec.rb b/spec/models/subscribe_spec.rb new file mode 100644 index 000000000..7d496da07 --- /dev/null +++ b/spec/models/subscribe_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' +require "cancan/matchers" + +def set_testable_data + @ability = Ability.new(@user) + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) +end + +describe Subscribe do + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create subscribe' do + @ability.should be_able_to(:create, Subscribe.new(:subscribeable => @issue, :user => @user)) + end + + context 'destroy' do + before(:each) do + @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + @stranger_subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @stranger) + end + + context 'own subscribe' do + it 'should destroy subscribe' do + @ability.should be_able_to(:destroy, @subscribe) + end + end + + context 'stranger subscribe' do + it 'should not destroy subscribe' do + @ability.should_not be_able_to(:destroy, @stranger_subscribe) + end + end + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create subscribe' do + @ability.should be_able_to(:create, Subscribe.new(:subscribeable => @issue, :user => @user)) + end + + context 'destroy' do + before(:each) do + @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + @stranger_subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @stranger) + end + + context 'own subscribe' do + it 'should destroy subscribe' do + @ability.should be_able_to(:destroy, @subscribe) + end + end + + context 'stranger subscribe' do + it 'should not destroy subscribe' do + @ability.should_not be_able_to(:destroy, @stranger_subscribe) + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e15db9c3b..a79b3ce74 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -37,9 +37,9 @@ def stub_rsync_methods any_instance_of(Platform, :umount_directory_for_rsync => true) end +Delayed::Worker.delay_jobs = false # Execute all jobs realtime + # Add testing root_path %x(rm -Rf #{Rails.root}/tmp/test_root) %x(mkdir -p #{Rails.root}/tmp/test_root) APP_CONFIG['root_path'] = "#{Rails.root}/tmp/test_root" - -