Added a new migration that creates the mapping table for joining epics
to their messages.
Added a business rule, Epic.can_comment?. It only returns true if the
given user has a role in one of the products owned by the epic's
project. To support this a new business rule, Project.is_member?, was
added that returns whether a user is a member of project's product
teams.
When an epic is shown, an empty comment is created but not saved if the
user is allowed to post comments. On the epic detail page a comment form
is shown.
Added a new route, comment_epic_path, for posting comments. Added the
supporting action, EpicsController.comment, to receive and process the
comment.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/epics_controller.rb | 49 ++++++++++++---
app/models/epic.rb | 6 ++
app/models/product_role.rb | 1 +
app/models/project.rb | 5 ++
app/views/epics/show.html.erb | 8 +++
config/routes.rb | 5 +-
db/migrate/023_create_epics_messages.rb | 36 +++++++++++
doc/ChangeLog | 1 +
test/fixtures/epics_messages.yml | 3 +
test/fixtures/messages.yml | 5 ++
test/functional/epics_controller_test.rb | 97 ++++++++++++++++++++++++------
test/unit/epic_test.rb | 21 +++++++
test/unit/project_test.rb | 35 +++++++++--
13 files changed, 235 insertions(+), 37 deletions(-)
create mode 100644 db/migrate/023_create_epics_messages.rb
create mode 100644 test/fixtures/epics_messages.yml
diff --git a/app/controllers/epics_controller.rb b/app/controllers/epics_controller.rb
index 5835f22..1d478c5 100644
--- a/app/controllers/epics_controller.rb
+++ b/app/controllers/epics_controller.rb
@@ -16,16 +16,18 @@
# +EpicsController+ allows users to work with +Epic+ stories.
class EpicsController < ApplicationController
- before_filter :authenticated, :except => [:index, :show]
- before_filter :load_project, :only => [:new, :create]
- before_filter :load_epic, :except => [:index, :new, :create]
- before_filter :verify_can_create, :only => [:new, :create]
- before_filter :verify_can_edit, :only => [:edit, :update]
- before_filter :verify_can_delete, :only => [:destroy]
- before_filter :verify_can_close, :only => [:close]
- before_filter :verify_can_reopen, :only => [:reopen]
- before_filter :path_to_list, :only => [:index, :new, :create]
- before_filter :path_to_one, :only => [:show, :edit, :update]
+ before_filter :authenticated, :except => [:index, :show]
+ before_filter :load_project, :only => [:new, :create]
+ before_filter :load_epic, :except => [:index, :new, :create]
+ before_filter :verify_can_create, :only => [:new, :create]
+ before_filter :verify_can_comment, :only => [:comment]
+ before_filter :verify_can_edit, :only => [:edit, :update]
+ before_filter :verify_can_delete, :only => [:destroy]
+ before_filter :verify_can_close, :only => [:close]
+ before_filter :verify_can_reopen, :only => [:reopen]
+ before_filter :load_comment, :only => [:comment]
+ before_filter :path_to_list, :only => [:index, :new, :create]
+ before_filter :path_to_one, :only => [:show, :edit, :update]
# GET /epics
def index
@@ -42,6 +44,7 @@ class EpicsController < ApplicationController
# GET /epics/1
def show
@title = "Epic:#{@epic.title}"
+ @comment = Message.new if @epic.can_comment?(@user)
end
# GET /epics/new
@@ -137,6 +140,24 @@ class EpicsController < ApplicationController
end
end
+ # POST /epics/1/comment
+ def comment
+ Epic.transaction do
+ respond_to do |format|
+ @comment.author = @user
+ @comment.posted = Time.current
+ if @comment.valid?
+ @epic.comments << @comment
+ @epic.save!
+ flash[:message] = "Comment saved."
+ format.html {redirect_to epic_path(@epic)}
+ else
+ format.html {render :action => :show}
+ end
+ end
+ end
+ end
+
private
def load_project
@@ -156,6 +177,10 @@ class EpicsController < ApplicationController
report_error "You are not allowed to created epics for this project."
unless @project.can_create_epics?(@user)
end
+ def verify_can_comment
+ report_error("You are not allowed to comment on this epic.",
epic_path(@epic)) unless @epic.can_comment?(@user)
+ end
+
def verify_can_edit
report_error "You are not allowed to edit this epic." unless
@epic.can_edit?(@user)
end
@@ -172,6 +197,10 @@ class EpicsController < ApplicationController
report_error "You are not allowed to reopen this epic." unless
@epic.can_reopen?(@user)
end
+ def load_comment
+ @comment = Message.new(params[:message])
+ end
+
def path_to_list
if @project || params[:project]
@project = Project.find_by_id(params[:project]) unless @project
diff --git a/app/models/epic.rb b/app/models/epic.rb
index d2e65a7..226a9f1 100644
--- a/app/models/epic.rb
+++ b/app/models/epic.rb
@@ -34,6 +34,7 @@ class Epic < ActiveRecord::Base
belongs_to :project
has_many :user_stories
+ has_and_belongs_to_many :comments, :class_name => "Message", :join_table
=> "epics_messages"
named_scope :for_project, lambda { |project_id|
{
@@ -46,6 +47,11 @@ class Epic < ActiveRecord::Base
"#{title} (#{priority})"
end
+ # Returns whether the user can post comments on this epic.
+ def can_comment?(user)
+ project.is_member?(user)
+ end
+
# Returns whether the user can edit this epic.
def can_edit?(user)
is_owner? user
diff --git a/app/models/product_role.rb b/app/models/product_role.rb
index fe77f43..f2824ef 100644
--- a/app/models/product_role.rb
+++ b/app/models/product_role.rb
@@ -32,6 +32,7 @@ class ProductRole < ActiveRecord::Base
belongs_to :user
belongs_to :role
+ named_scope :for_project, lambda {|project_id| {:conditions => ['product_id in
(select id from products where project_id = ?)', project_id]}}
named_scope :for_product, lambda {|product_id| {:conditions => ['product_id =
?', product_id]}}
named_scope :approved, {:conditions => 'is_approved = true and pending =
false'}
named_scope :for_user, lambda {|user_id| {:conditions => ['user_id = ?',
user_id]}}
diff --git a/app/models/project.rb b/app/models/project.rb
index 96ddf10..00f90c8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -43,6 +43,11 @@ class Project < ActiveRecord::Base
}
}
+ # Returns whether the user is a member of one of the project's product teams.
+ def is_member?(user)
+ user && !ProductRole.for_project(self).for_user(user).approved.empty?
+ end
+
# Returns whether the user can modify this project.
def can_edit?(user)
user && (user.privileges.admin_projects || (user.id == owner_id))
diff --git a/app/views/epics/show.html.erb b/app/views/epics/show.html.erb
index ea14757..713f55a 100644
--- a/app/views/epics/show.html.erb
+++ b/app/views/epics/show.html.erb
@@ -7,6 +7,14 @@
<dt><%= "This epic is #{(a)epic.closed ? 'closed' :
'open'}." %></dt>
</dl>
</div>
+
+ <% if @epic.can_comment?(@user) %>
+ <%= render :partial => 'messages/edit',
+ :locals => {:caption => "Comment On This Epic", :url =>
comment_epic_path(@epic),:message => @comment} %>
+ <% end %>
+
+ <%= render :partial => 'messages/list', :locals => {:caption =>
"Comments", :messages => @epic.comments} %>
+
</div>
<% render :layout => 'home/sidebar', :locals => {:title => 'Epic
Commands'} do %>
diff --git a/config/routes.rb b/config/routes.rb
index 4c62ef3..143a207 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -30,8 +30,9 @@ ActionController::Routing::Routes.draw do |map|
map.resources :epics, :member =>
{
- :close => :put,
- :reopen => :put
+ :close => :put,
+ :reopen => :put,
+ :comment => :post
}
map.resources :projects, :member =>
diff --git a/db/migrate/023_create_epics_messages.rb
b/db/migrate/023_create_epics_messages.rb
new file mode 100644
index 0000000..3669c18
--- /dev/null
+++ b/db/migrate/023_create_epics_messages.rb
@@ -0,0 +1,36 @@
+# 023_create_epics_messages.rb
+# Copyright (C) 2009, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+class CreateEpicsMessages < ActiveRecord::Migration
+ def self.up
+ create_table :epics_messages do |t|
+ t.integer :epic_id, :null => false
+ t.integer :message_id, :null => false
+ end
+
+ add_index :epics_messages, :message_id, :unique => true
+
+ execute 'alter table epics_messages add constraint fk_epics_messages_epic
+ foreign key (epic_id) references epics(id)'
+ execute 'alter table epics_messages add constraint fk_epics_messages_message
+ foreign key (message_id) references messages(id)'
+ end
+
+ def self.down
+ remove_table :epics_messages
+ end
+end
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 2abdbaa..232b8e8 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -5,6 +5,7 @@ Change Log (0.3.0):
* #160 - A product's RSS feed is displayed on the product details page.
* #161 - Users can post comments against user stories.
* #167 - Blocker messages are included in the daily updates email.
+ * #168 - Users can post comments for epic stories.
* #173 - Backlog items can be dropped from an active sprint.
* #174 - Deferred backlog items can be re-added to the active sprint.
* #175 - When viewing an unapproved project's product list, the sidebar is
misplaced. (BUG)
diff --git a/test/fixtures/epics_messages.yml b/test/fixtures/epics_messages.yml
new file mode 100644
index 0000000..8cd286f
--- /dev/null
+++ b/test/fixtures/epics_messages.yml
@@ -0,0 +1,3 @@
+epic_comment:
+ epic_id: <%= Fixtures.identify(:user_stories_epic) %>
+ message_id: <%= Fixtures.identify(:epic_message) %>
diff --git a/test/fixtures/messages.yml b/test/fixtures/messages.yml
index 55984a6..8db897b 100644
--- a/test/fixtures/messages.yml
+++ b/test/fixtures/messages.yml
@@ -1,3 +1,8 @@
+epic_message:
+ author_id: <%= Fixtures.identify(:mcpierce) %>
+ posted: <%= Time.current %>
+ body: This is a message against an epic story.
+
user_story_message:
author_id: <%= Fixtures.identify(:mcpierce) %>
posted: <%= Time.current %>
diff --git a/test/functional/epics_controller_test.rb
b/test/functional/epics_controller_test.rb
index ebf1313..69357be 100644
--- a/test/functional/epics_controller_test.rb
+++ b/test/functional/epics_controller_test.rb
@@ -45,10 +45,16 @@ class EpicsControllerTest < ActionController::TestCase
raise "Project cannot be approved!" if @unapproved_project.approved
raise "Project owner is wrong!" unless @unapproved_project.owner_id ==
@owner.id
+ @member = users(:mcpierce)
+ raise "Member must have a product role!" unless
@project.is_member?(@member)
+ @nonmember = users(:celliot)
+ raise "Nonmember must not have a product role!" if
@project.is_member?(@nonmember)
+
@nonowner = users(:mcpierce)
raise "Nonowner and owner cannot be the same user!" if @owner.id ==
@nonowner.id
- @new_epic = { :title => 'This is a new epic', :priority => 1 }
+ @new_epic = {:title => 'This is a new epic', :priority => 1}
+ @new_comment = {:body => "This is a comment"}
end
# Ensures that viewing a list of epics requires a valid project.
@@ -60,12 +66,12 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that viewing epics for a specific project limits those displayed.
def test_index_with_project
- get :index, { :project => @project.id }
+ get :index, {:project => @project.id}
assert_response :success
assert_template 'epics/index'
assert assigns['epics'], "Failed to load any epics."
- assigns['epics'].each { |epic| assert_equal @project.id, epic.project_id,
"Only those epics for the specified project should have been loaded." }
+ assigns['epics'].each {|epic| assert_equal @project.id, epic.project_id,
"Only those epics for the specified project should have been loaded."}
end
# Ensures that viewing a list of epics works as expected.
@@ -78,18 +84,28 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a valid epic id is required.
def test_show_with_invalid_epic
- get :show, { }
+ get :show, {}
assert_redirected_to error_path
end
# Ensures that showing an epic works as expected.
def test_show
- get :show, { :id => @epic.id }
+ get :show, {:id => @epic.id}
+
+ assert_response :success
+ assert assigns['epic'], "Failed to load the epic."
+ assert_equal @epic.id, assigns['epic'].id, "Loaded the wrong
epic."
+ end
+
+ # Ensures that showing an epic for a team member gets a comment.
+ def test_show_for_member
+ get :show, {:id => @epic.id}, {:user_id => @member.id}
assert_response :success
assert assigns['epic'], "Failed to load the epic."
assert_equal @epic.id, assigns['epic'].id, "Loaded the wrong
epic."
+ assert assigns['comment'], "A comment should have been created."
end
# Ensures that anonymous users cannot create new epics.
@@ -101,7 +117,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a valid project is required to create an epic.
def test_new_with_invalid_project
- get :new, { }, {:user_id => @owner.id}
+ get :new, {}, {:user_id => @owner.id}
assert_redirected_to error_path
end
@@ -115,7 +131,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that creating a new epic works as expected.
def test_new
- get :new, { :project => @project.id }, {:user_id => @owner.id}
+ get :new, {:project => @project.id}, {:user_id => @owner.id}
assert_response :success
assert assigns['epic'], "Failed to create a new epic."
@@ -130,28 +146,28 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a valid project is required to create a new epic.
def test_create_with_invalid_project
- post :create, { }, {:user_id => @owner.id}
+ post :create, {}, {:user_id => @owner.id}
assert_redirected_to error_path
end
# Ensures that an unapproved project cannot have epics created.
def test_create_for_unapproved_project
- post :create, { :project => @unapproved_project.id, :epic => @new_epic},
{:user_id => @owner.id}
+ post :create, {:project => @unapproved_project.id, :epic => @new_epic},
{:user_id => @owner.id}
assert_redirected_to error_path
end
# Ensures that a non-owner cannot create a new epic.
def test_create_as_nonowner
- post :create, {:project => @project.id, :epic => @new_epic }, {:user_id =>
@nonowner.id}
+ post :create, {:project => @project.id, :epic => @new_epic}, {:user_id =>
@nonowner.id}
assert_redirected_to error_path
end
# Ensures that a malformed epic redisplays the edit window.
def test_create_with_invalid_epic
- post :create, {:project => @project.id, :epic => {:title =>
"Test"} }, {:user_id => @owner.id}
+ post :create, {:project => @project.id, :epic => {:title =>
"Test"}}, {:user_id => @owner.id}
assert_response :success
assert_template "edit"
@@ -159,7 +175,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a well-formed epic is saved.
def test_create
- post :create, { :project => @project.id, :epic => @new_epic }, {:user_id =>
@owner.id}
+ post :create, {:project => @project.id, :epic => @new_epic}, {:user_id =>
@owner.id}
result = Epic.find_by_title(@new_epic[:title])
assert result, "The new epic was not saved."
@@ -175,7 +191,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a valid epic id is required.
def test_edit_with_invalid_epic
- get :edit, { }, {:user_id => @owner.id}
+ get :edit, {}, {:user_id => @owner.id}
assert_redirected_to error_path
end
@@ -236,7 +252,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that epics are updated as expected.
def test_update
put :update,
- { :id => @epic.id, :epic => {:title => "test"}},
+ {:id => @epic.id, :epic => {:title => "test"}},
{:user_id => @owner.id}
assert_redirected_to epic_path(@epic)
@@ -292,7 +308,7 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that only the owner can mark a project completed.
def test_close_as_nonowner
- put :close, { :id => @epic.id}, {:user_id => @nonowner.id}
+ put :close, {:id => @epic.id}, {:user_id => @nonowner.id}
assert_redirected_to error_path
end
@@ -321,21 +337,21 @@ class EpicsControllerTest < ActionController::TestCase
# Ensures that a valid epic id is required.
def test_reopen_with_invalid_epic
- put :reopen, { }, {:user_id => @owner.id}
+ put :reopen, {}, {:user_id => @owner.id}
assert_redirected_to error_path
end
# Ensures that only the owner can reopen a closed epic.
def test_reopen_as_nonowner
- put :reopen, { :id => @closed_epic.id}, {:user_id => @nonowner.id}
+ put :reopen, {:id => @closed_epic.id}, {:user_id => @nonowner.id}
assert_redirected_to error_path
end
# Ensures that only an open epic cannot be reopened.
def test_reopen_with_open_epic
- put :reopen, { :id => @epic.id}, {:user_id => @owner.id}
+ put :reopen, {:id => @epic.id}, {:user_id => @owner.id}
assert_redirected_to error_path
end
@@ -347,4 +363,49 @@ class EpicsControllerTest < ActionController::TestCase
assert_redirected_to epic_path(@closed_epic)
assert !Epic.find_by_id((a)closed_epic.id).closed, "Epic should have been
reopened."
end
+
+ # Ensures that anonymous user's cannot post comments.
+ def test_comment_as_anonymous
+ post :comment, {:message => @new_comment}
+
+ assert_redirected_to login_path
+ end
+
+ # Ensures that a valid id is required.
+ def test_comment_with_invalid_id
+ post :comment, {:message => @new_comment}, {:user_id => @member.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that a nonmember cannot comment on epics.
+ def test_comment_as_nonmember
+ count = @epic.comments.size
+ post :comment, {:id => @epic.id, :message => @new_comment}, {:user_id =>
@nonmember.id}
+
+ assert_redirected_to epic_path(@epic)
+ result = Epic.find_by_id((a)epic.id)
+ assert_equal count, result.comments.size, "Comment should not have been
posted."
+ end
+
+ # Ensures that a bad comment is redisplayed.
+ def test_comment_with_invalid_comment
+ count = @epic.comments.size
+ @new_comment[:body] = ''
+ post :comment, {:id => @epic.id, :message => @new_comment},{:user_id =>
@member.id}
+
+ assert_template 'epics/show'
+ result = Epic.find_by_id((a)epic.id)
+ assert_equal count, result.comments.size, "Comment should not have been
posted."
+ end
+
+ # Ensures that posting a comment works as expected.
+ def test_comment
+ count = @epic.comments.size
+ post :comment, {:id => @epic.id, :message => @new_comment}, {:user_id =>
@member.id}
+
+ assert_redirected_to epic_path(@epic)
+ result = Epic.find_by_id((a)epic.id)
+ assert_equal count + 1, result.comments.size, "Comment should have been
saved."
+ end
end
diff --git a/test/unit/epic_test.rb b/test/unit/epic_test.rb
index daefae4..e644309 100644
--- a/test/unit/epic_test.rb
+++ b/test/unit/epic_test.rb
@@ -34,6 +34,12 @@ class EpicTest < ActiveSupport::TestCase
raise "Epic must be part of the project!" unless @epic.project_id ==
@project.id
raise "Epic must be open!" if @epic.closed
+ @member = users(:mcpierce)
+ raise "Member must have a product member!" unless
@epic.project.is_member?(@member)
+
+ @nonmember = users(:celliot)
+ raise "Non-member cannot be a part of any product team!" if
@epic.project.is_member?(@nonmember)
+
@closed_epic = epics(:closed_epic)
raise "Epic must be closed!" unless @closed_epic.closed
@@ -145,4 +151,19 @@ class EpicTest < ActiveSupport::TestCase
def test_can_reopen
flunk "Closed tasks should be able to reopen." unless
@closed_epic.can_reopen?(@owner)
end
+
+ # Ensures that an anonymous user cannot comment on epics.
+ def test_can_comment_fails_for_anonymous
+ flunk "Anonymous users cannot comment on epics." if
@epic.can_comment?(nil)
+ end
+
+ # Ensures that users who aren't members of a project's products teams cannot
comment.
+ def test_can_comment_as_nonmember
+ flunk "Non-product members cannot comment on epics." if
@epic.can_comment?(@nonmember)
+ end
+
+ # Ensures that members can comment on epics.
+ def test_can_comment
+ flunk "Members must be able to comment on epics." unless
@epic.can_comment?(@member)
+ end
end
diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb
index 56d70f9..65ff86e 100644
--- a/test/unit/project_test.rb
+++ b/test/unit/project_test.rb
@@ -22,11 +22,11 @@ class ProjectTest < ActiveSupport::TestCase
fixtures :users
def setup
- @existing_project = projects(:projxp)
+ @project = projects(:projxp)
@unapproved_project = projects(:unapproved_project)
raise "Unapproved project cannot be approved!" if
@unapproved_project.approved
- @owner = @existing_project.owner
+ @owner = @project.owner
@nonowner = users(:mcpierce)
raise "Owner and nonowner cannot be the same person!" if @owner.id ==
@nonowner.id
@@ -35,6 +35,12 @@ class ProjectTest < ActiveSupport::TestCase
@nonadmin = users(:mcpierce)
raise "Nonadmin must not have project admin rights!" if
@nonadmin.privileges.admin_projects
+ @member = ProductRole.for_product((a)project.products.first).approved.first.user
+ raise "No product member found!" unless @member
+
+ @nonmember = users(:celliot)
+ Product.for_project((a)project).each {|product| raise "Nonmember cannot have
product role!" if product.is_member?(@nonmember)}
+
@project_with_logo = projects(:projxp)
raise "Project must have a logo!" unless @project_with_logo.logo_name
@project_without_logo = projects(:teatime)
@@ -55,7 +61,7 @@ class ProjectTest < ActiveSupport::TestCase
# Ensures that a project must have a unique name.
def test_valid_fails_without_unique_name
- @new_project.name = @existing_project.name
+ @new_project.name = @project.name
flunk 'A project must have a unique name.' if @new_project.valid?
end
@@ -79,12 +85,12 @@ class ProjectTest < ActiveSupport::TestCase
# Ensures non-owners cannot create products.
def test_create_products_for_nonowners
- flunk "Non-owners should not be able to create products!" if
@existing_project.can_create_products?(@nonowner)
+ flunk "Non-owners should not be able to create products!" if
@project.can_create_products?(@nonowner)
end
# Ensures that a project owner can create products.
def test_create_products_for_project_owner
- flunk "A project owner should be able to create products." unless
@existing_project.can_create_products?(@owner)
+ flunk "A project owner should be able to create products." unless
@project.can_create_products?(@owner)
end
# Ensures that non admins cannot approve projects.
@@ -104,12 +110,12 @@ class ProjectTest < ActiveSupport::TestCase
# Ensures that non-admins cannot create epics.
def test_create_epics_for_nonadmins
- flunk "Non-admins cannot create epics." if
@existing_project.can_create_epics?(@nonowner)
+ flunk "Non-admins cannot create epics." if
@project.can_create_epics?(@nonowner)
end
# Ensures that creating epics checks is fine.
def test_create_epics
- flunk "A project owner can create epics." unless
@existing_project.can_create_epics?(@owner)
+ flunk "A project owner can create epics." unless
@project.can_create_epics?(@owner)
end
# Ensures that a project with no logo returns the default logo.
@@ -117,4 +123,19 @@ class ProjectTest < ActiveSupport::TestCase
expected = ConfigProperty.fetch("image.default_project_logo")
assert_equal expected, @project_without_logo.logo_url, "The default URL is not
correct."
end
+
+ # Ensures that anonymous users aren't listed as members.
+ def test_is_member_for_anonymous
+ flunk "Anonymous users are not members." if @project.is_member?(nil)
+ end
+
+ # Ensures that users who are not members of a project's products are not listed as
members.
+ def test_is_member_for_nonmember
+ flunk "This user is not a member!" if @project.is_member?(@nonmember)
+ end
+
+ # Ensures that membership is determined correctly.
+ def test_is_member
+ flunk "This user is a member!" unless @project.is_member?(@member)
+ end
end
--
1.6.0.6