canvas-lms/spec/models/group_spec.rb

639 lines
23 KiB
Ruby

#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe Group do
before :once do
course_model
group_model(:context => @course)
end
context "validation" do
it "should create a new instance given valid attributes" do
group_model
end
end
it "should have a wiki" do
@group.wiki.should_not be_nil
end
it "should be private by default" do
@group.is_public.should be_false
end
it "should allow a private group to be made public" do
@communities = GroupCategory.communities_for(Account.default)
group_model(:group_category => @communities, :is_public => false)
@group.is_public = true
@group.save!
@group.reload.is_public.should be_true
end
it "should not allow a public group to be made private" do
@communities = GroupCategory.communities_for(Account.default)
group_model(:group_category => @communities, :is_public => true)
@group.is_public = false
@group.save.should be_false
@group.reload.is_public.should be_true
end
it 'delegates time_zone through to its context' do
zone = ActiveSupport::TimeZone["America/Denver"]
@course.time_zone = zone
@group.time_zone.should =~ /Mountain Time/
end
context "#peer_groups" do
it "should find all peer groups" do
context = course_model
group_category = context.group_categories.create(:name => "worldCup")
other_category = context.group_categories.create(:name => "other category")
group1 = Group.create!(:name=>"group1", :group_category => group_category, :context => context)
group2 = Group.create!(:name=>"group2", :group_category => group_category, :context => context)
group3 = Group.create!(:name=>"group3", :group_category => group_category, :context => context)
group4 = Group.create!(:name=>"group4", :group_category => other_category, :context => context)
group1.peer_groups.length.should == 2
group1.peer_groups.should be_include(group2)
group1.peer_groups.should be_include(group3)
group1.peer_groups.should_not be_include(group1)
group1.peer_groups.should_not be_include(group4)
end
it "should not find peer groups for student organized groups" do
context = course_model
group_category = GroupCategory.student_organized_for(context)
group1 = Group.create!(:name=>"group1", :group_category=>group_category, :context => context)
group2 = Group.create!(:name=>"group2", :group_category=>group_category, :context => context)
group1.peer_groups.should be_empty
end
end
context "atom" do
it "should have an atom name as it's own name" do
group_model(:name => 'some unique name')
@group.to_atom.title.should eql('some unique name')
end
it "should have a link to itself" do
link = @group.to_atom.links.first.to_s
link.should eql("/groups/#{@group.id}")
end
end
context "add_user" do
it "should be able to add a person to the group" do
user_model
pseudonym_model(:user_id => @user.id)
@group.add_user(@user)
@group.users.should be_include(@user)
end
it "shouldn't be able to add a person to the group twice" do
user_model
pseudonym_model(:user_id => @user.id)
@group.add_user(@user)
@group.users.should be_include(@user)
@group.users.count.should == 1
@group.add_user(@user)
@group.reload
@group.users.should be_include(@user)
@group.users.count.should == 1
end
it "should remove that user from peer groups" do
context = course_model
group_category = context.group_categories.create!(:name => "worldCup")
group1 = Group.create!(:name=>"group1", :group_category=>group_category, :context => context)
group2 = Group.create!(:name=>"group2", :group_category=>group_category, :context => context)
user_model
pseudonym_model(:user_id => @user.id)
group1.add_user(@user)
group1.users.should be_include(@user)
group2.add_user(@user)
group2.users.should be_include(@user)
group1.reload
group1.users.should_not be_include(@user)
end
it "should add a user at the right workflow_state by default" do
@communities = GroupCategory.communities_for(Account.default)
user_model
{
'invitation_only' => 'invited',
'parent_context_request' => 'requested',
'parent_context_auto_join' => 'accepted'
}.each do |join_level, workflow_state|
group = group_model(:join_level => join_level, :group_category => @communities)
group.add_user(@user)
group.group_memberships.where(:workflow_state => workflow_state, :user_id => @user).first.should_not be_nil
end
end
it "should allow specifying a workflow_state" do
@communities = GroupCategory.communities_for(Account.default)
@group.group_category = @communities
@group.save!
user_model
[ 'invited', 'requested', 'accepted' ].each do |workflow_state|
@group.add_user(@user, workflow_state)
@group.group_memberships.where(:workflow_state => workflow_state, :user_id => @user).first.should_not be_nil
end
end
it "should allow specifying that the user should be a moderator" do
user_model
@membership = @group.add_user(@user, 'accepted', true)
@membership.moderator.should == true
end
it "should change the workflow_state of an already active user" do
@communities = GroupCategory.communities_for(Account.default)
@group.group_category = @communities
@group.save!
user_model
@group.add_user(@user, 'accepted')
@membership = @group.add_user(@user, 'requested')
@membership.workflow_state.should == 'accepted'
end
end
it "should grant manage permissions for associated objects to group managers" do
e = course_with_teacher
course = e.context
teacher = e.user
group = course.groups.create
course.grants_right?(teacher, :manage_groups).should be_true
group.grants_right?(teacher, :manage_wiki).should be_true
group.grants_right?(teacher, :manage_files).should be_true
group.wiki.grants_right?(teacher, :update_page).should be_true
attachment = group.attachments.build
attachment.grants_right?(teacher, :create).should be_true
end
it "should only allow me to moderate_forum if I can moderate_forum of group's context" do
course_with_teacher
student_in_course
group = @course.groups.create
group.grants_right?(@teacher, :moderate_forum).should be_true
group.grants_right?(@student, :moderate_forum).should be_false
end
it "should grant read_roster permissions to students that can freely join or request an invitation to the group" do
course_with_teacher
student_in_course
# default join_level == 'invitation_only' and default category is not self-signup
group = @course.groups.create
group.grants_right?(@student, :read_roster).should be_false
# join_level allows requesting group membership
group = @course.groups.create(:join_level => 'parent_context_request')
group.grants_right?(@student, :read_roster).should be_true
# category is self-signup
category = @course.group_categories.build(name: 'category name')
category.self_signup = 'enabled'
category.save
group = @course.groups.create(:group_category => category)
group.grants_right?(@student, :read_roster).should be_true
end
describe "root account" do
it "should get the root account assigned" do
e = course_with_teacher
group = @course.groups.create!
group.account.should == Account.default
group.root_account.should == Account.default
new_root_acct = account_model
new_sub_acct = new_root_acct.sub_accounts.create!(:name => 'sub acct')
group.context = new_sub_acct
group.save!
group.account.should == new_sub_acct
group.root_account.should == new_root_acct
end
end
context "auto_accept?" do
it "should be false unless join level is 'parent_context_auto_join'" do
course_with_student
group_category = GroupCategory.student_organized_for(@course)
group1 = @course.groups.create(:group_category => group_category, :join_level => 'parent_context_auto_join')
group2 = @course.groups.create(:group_category => group_category, :join_level => 'parent_context_request')
group3 = @course.groups.create(:group_category => group_category, :join_level => 'invitation_only')
[group1, group2, group3].map{|g| g.auto_accept?}.should == [true, false, false]
end
it "should be false unless the group is student organized or a community" do
course_with_student
@account = @course.root_account
jl = 'parent_context_auto_join'
group1 = @course.groups.create(:group_category => @course.group_categories.create(:name => "random category"), :join_level => jl)
group2 = @course.groups.create(:group_category => GroupCategory.student_organized_for(@course), :join_level => jl)
group3 = @account.groups.create(:group_category => GroupCategory.communities_for(@account), :join_level => jl)
[group1, group2, group3].map{|g| g.auto_accept?}.should == [false, true, true]
end
end
context "allow_join_request?" do
it "should be false unless join level is 'parent_context_auto_join' or 'parent_context_request'" do
course_with_student
group_category = GroupCategory.student_organized_for(@course)
group1 = @course.groups.create(:group_category => group_category, :join_level => 'parent_context_auto_join')
group2 = @course.groups.create(:group_category => group_category, :join_level => 'parent_context_request')
group3 = @course.groups.create(:group_category => group_category, :join_level => 'invitation_only')
[group1, group2, group3].map{|g| g.allow_join_request?}.should == [true, true, false]
end
it "should be false unless the group is student organized or a community" do
course_with_student
@account = @course.root_account
jl = 'parent_context_auto_join'
group1 = @course.groups.create(:group_category => @course.group_categories.create(:name => "random category"), :join_level => jl)
group2 = @course.groups.create(:group_category => GroupCategory.student_organized_for(@course), :join_level => jl)
group3 = @account.groups.create(:group_category => GroupCategory.communities_for(@account), :join_level => jl)
[group1, group2, group3].map{|g| g.allow_join_request?}.should == [false, true, true]
end
end
context "allow_self_signup?" do
it "should follow the group category self signup option" do
course_with_student
group_category = GroupCategory.student_organized_for(@course)
group_category.configure_self_signup(true, false)
group_category.save!
group1 = @course.groups.create(:group_category => group_category)
group1.allow_self_signup?(@student).should be_true
group_category.configure_self_signup(true, true)
group_category.save!
group2 = @course.groups.create(:group_category => group_category)
group2.allow_self_signup?(@student).should be_true
group_category.configure_self_signup(false, false)
group_category.save!
group3 = @course.groups.create(:group_category => group_category)
group3.allow_self_signup?(@student).should be_false
end
it "should correctly handle restricted course sections" do
course_with_student
@other_section = @course.course_sections.create!(:name => "Other Section")
@other_student = @course.enroll_student(user_model, {:section => @other_section}).user
group_category = GroupCategory.student_organized_for(@course)
group_category.configure_self_signup(true, true)
group_category.save!
group1 = @course.groups.create(:group_category => group_category)
group1.allow_self_signup?(@student).should be_true
group1.add_user(@student)
group1.reload
group1.allow_self_signup?(@other_student).should be_false
end
end
context "#full?" do
it "returns true when category group_limit has been met" do
@group.group_category = @course.group_categories.build(:name => 'foo')
@group.group_category.group_limit = 1
@group.add_user user_model, 'accepted'
@group.should be_full
end
it "returns true when max_membership has been met" do
@group.group_category = @course.group_categories.build(:name => 'foo')
@group.group_category.group_limit = 0
@group.max_membership = 1
@group.add_user user_model, 'accepted'
@group.should be_full
end
it "returns false when max_membership has not been met" do
@group.group_category = @course.group_categories.build(:name => 'foo')
@group.group_category.group_limit = 0
@group.max_membership = 2
@group.add_user user_model, 'accepted'
@group.should_not be_full
end
it "returns false when category group_limit has not been met" do
# no category
@group.should_not be_full
# not full
@group.group_category = @course.group_categories.build(:name => 'foo')
@group.group_category.group_limit = 2
@group.add_user user_model, 'accepted'
@group.should_not be_full
end
end
context "has_member?" do
it "should be true for accepted memberships, regardless of moderator flag" do
@user1 = user_model
@user2 = user_model
@user3 = user_model
@user4 = user_model
@user5 = user_model
@group.add_user(@user1, 'accepted')
@group.add_user(@user2, 'accepted')
@group.add_user(@user3, 'invited')
@group.add_user(@user4, 'requested')
@group.add_user(@user5, 'rejected')
GroupMembership.where(:group_id => @group, :user_id => @user2).update_all(:moderator => true)
@group.has_member?(@user1).should be_true
@group.has_member?(@user2).should be_true
@group.has_member?(@user3).should be_true # false when we turn auto_join off
@group.has_member?(@user4).should be_true # false when we turn auto_join off
@group.has_member?(@user5).should be_false
end
end
context "has_moderator?" do
it "should be true for accepted memberships, with moderator flag" do
@user1 = user_model
@user2 = user_model
@user3 = user_model
@user4 = user_model
@user5 = user_model
@group.add_user(@user1, 'accepted')
@group.add_user(@user2, 'accepted')
@group.add_user(@user3, 'invited')
@group.add_user(@user4, 'requested')
@group.add_user(@user5, 'rejected')
GroupMembership.where(:group_id => @group, :user_id => [@user2, @user3, @user4, @user5]).update_all(:moderator => true)
@group.has_moderator?(@user1).should be_false
@group.has_moderator?(@user2).should be_true
@group.has_moderator?(@user3).should be_true # false when we turn auto_join off
@group.has_moderator?(@user4).should be_true # false when we turn auto_join off
@group.has_moderator?(@user5).should be_false
end
end
context "invite_user" do
it "should auto accept invitations" do
course_with_student(:active_all => true)
group_category = GroupCategory.student_organized_for(@course)
group = @course.groups.create!(:group_category => group_category)
gm = group.invite_user(@student)
gm.should be_accepted
end
end
context "request_user" do
it "should auto accept invitations" do
course_with_student(:active_all => true)
group_category = GroupCategory.student_organized_for(@course)
group = @course.groups.create!(:group_category => group_category, :join_level => 'parent_context_auto_join')
gm = group.request_user(@student)
gm.should be_accepted
end
end
it "should default group_category to student organized category on save" do
course_with_teacher
group = @course.groups.create
group.group_category.should == GroupCategory.student_organized_for(@course)
group_category = @course.group_categories.create(:name => "random category")
group = @course.groups.create(:group_category => group_category)
group.group_category.should == group_category
end
it "as_json should include group_category" do
course()
gc = group_category(name: "Something")
group = Group.create(:group_category => gc)
hash = group.as_json
hash["group"]["group_category"].should == "Something"
end
it "should maintain the deprecated category attribute" do
course = course_model
group = course.groups.create
default_category = GroupCategory.student_organized_for(course)
group.read_attribute(:category).should eql(default_category.name)
group.group_category = group.context.group_categories.create(:name => "my category")
group.save
group.reload
group.read_attribute(:category).should eql("my category")
group.group_category = nil
group.save
group.reload
group.read_attribute(:category).should eql(default_category.name)
end
context "has_common_section?" do
it "should be false for accounts" do
account = Account.default
group = account.groups.create
group.should_not have_common_section
end
it "should not be true if two members don't share a section" do
course_with_teacher(:active_all => true)
section1 = @course.course_sections.create
section2 = @course.course_sections.create
user1 = section1.enroll_user(user_model, 'StudentEnrollment').user
user2 = section2.enroll_user(user_model, 'StudentEnrollment').user
group = @course.groups.create
group.add_user(user1)
group.add_user(user2)
group.should_not have_common_section
end
it "should be true if all members group have a section in common" do
course_with_teacher(:active_all => true)
section1 = @course.course_sections.create
user1 = section1.enroll_user(user_model, 'StudentEnrollment').user
user2 = section1.enroll_user(user_model, 'StudentEnrollment').user
group = @course.groups.create
group.add_user(user1)
group.add_user(user2)
group.should have_common_section
end
end
context "has_common_section_with_user?" do
it "should be false for accounts" do
account = Account.default
group = account.groups.create
group.should_not have_common_section_with_user(user_model)
end
it "should not be true if the new member does't share a section with an existing member" do
course_with_teacher(:active_all => true)
section1 = @course.course_sections.create
section2 = @course.course_sections.create
user1 = section1.enroll_user(user_model, 'StudentEnrollment').user
user2 = section2.enroll_user(user_model, 'StudentEnrollment').user
group = @course.groups.create
group.add_user(user1)
group.should_not have_common_section_with_user(user2)
end
it "should be true if all members group have a section in common with the new user" do
course_with_teacher(:active_all => true)
section1 = @course.course_sections.create
user1 = section1.enroll_user(user_model, 'StudentEnrollment').user
user2 = section1.enroll_user(user_model, 'StudentEnrollment').user
group = @course.groups.create
group.add_user(user1)
group.should have_common_section_with_user(user2)
end
end
context "tabs_available" do
before :once do
course_with_teacher
@teacher = @user
@group = group(:group_context => @course)
@group.users << @student = student_in_course(:course => @course).user
end
it "should let members see everything" do
@group.tabs_available(@student).map{|t|t[:id]}.should eql [
Group::TAB_HOME,
Group::TAB_ANNOUNCEMENTS,
Group::TAB_PAGES,
Group::TAB_PEOPLE,
Group::TAB_DISCUSSIONS,
Group::TAB_FILES,
Group::TAB_CONFERENCES,
Group::TAB_COLLABORATIONS,
]
end
it "should let admins see everything" do
@group.tabs_available(@teacher).map{|t|t[:id]}.should eql [
Group::TAB_HOME,
Group::TAB_ANNOUNCEMENTS,
Group::TAB_PAGES,
Group::TAB_PEOPLE,
Group::TAB_DISCUSSIONS,
Group::TAB_FILES,
Group::TAB_CONFERENCES,
Group::TAB_COLLABORATIONS,
]
end
it "should not let nobodies see conferences" do
@group.tabs_available(nil).map{|t|t[:id]}.should_not include Group::TAB_CONFERENCES
end
end
describe "quota" do
it "should default to Group.default_storage_quota" do
@group.quota.should == Group.default_storage_quota
end
it "should be overridden by the account's default_group_storage_quota" do
a = @group.account
a.default_group_storage_quota = 10.megabytes
a.save!
@group.reload
@group.quota.should == 10.megabytes
end
end
describe "#feature_enabled?" do
before(:once) do
course_with_teacher(active_all: true)
@course.root_account.allow_feature!(:draft_state)
end
context "a course with :draft_state enabled" do
it "should pass its setting on to its groups" do
@course.enable_feature!(:draft_state)
group(group_context: @course).should be_feature_enabled(:draft_state)
end
end
context "an account with :draft_state enabled" do
before :once do
@course.root_account.enable_feature!(:draft_state)
end
it "should pass its setting on to course groups" do
group(group_context: @course).should be_feature_enabled(:draft_state)
end
it "should pass its setting on to account groups" do
group(group_context: @course.root_account).should be_feature_enabled(:draft_state)
end
end
end
describe "#update_max_membership_from_group_category" do
it "should set max_membership if there is a group category" do
@group.group_category = @course.group_categories.build(:name => 'foo')
@group.group_category.group_limit = 1
@group.update_max_membership_from_group_category
@group.max_membership.should == 1
end
it "should do nothing if there is no group category" do
@group.max_membership.should be_nil
@group.update_max_membership_from_group_category
@group.max_membership.should be_nil
end
end
describe '#destroy' do
before :once do
@gc = GroupCategory.create! name: "groups"
@group = @gc.groups.create! name: "group1", context: @course
end
it "should soft delete" do
@group.deleted_at.should be_nil
@group.destroy
@group.deleted_at.should_not be_nil
end
it "should not delete memberships" do
student_in_course active_all: true
@group.users << @student
@group.save!
@group.users.should == [@student]
@group.destroy
@group.users(true).should == [@student]
end
end
end