RSpec.configure do |config| config.order = "random" config.before(:suite) do DatabaseCleaner[:mongoid].strategy = :truncation end config.before(:each) do DatabaseCleaner[:mongoid].start end config.after(:each) do DatabaseCleaner[:mongoid].clean end end require 'mongoid' puts "Mongoid version #{Mongoid::VERSION}" client_configuration = { default: { database: 'groupify', hosts: [ "localhost:27017" ] } } # Load mongoid config if Mongoid::VERSION < "5" Mongoid::Config.sessions = client_configuration Moped.logger = Logger.new(STDOUT) if DEBUG else if DEBUG Mongoid::Config.log_level = :debug Mongoid.logger.level = ::Logger::DEBUG else Mongoid::Config.log_level = :warn Mongoid.logger.level = ::Logger::WARN end Mongoid::Config.load_configuration(clients: client_configuration) end require 'groupify/adapter/mongoid' class MongoidUser include Mongoid::Document groupify :group_member, group_class_name: 'MongoidGroup' groupify :named_group_member end class MongoidManager < MongoidUser end class MongoidWidget include Mongoid::Document groupify :group_member, group_class_name: 'MongoidGroup' end class MongoidTask include Mongoid::Document groupify :group_member, group_class_name: 'MongoidGroup' end class MongoidIssue include Mongoid::Document groupify :group_member, group_class_name: 'MongoidProject' end class MongoidGroup include Mongoid::Document groupify :group, members: [:mongoid_users, :mongoid_tasks, :mongoid_widgets, :mongoid_groups], default_members: :mongoid_users groupify :group_member, group_class_name: "MongoidGroup" alias_method :users, :mongoid_users alias_method :tasks, :mongoid_tasks alias_method :widgets, :mongoid_widgets end class MongoidProject < MongoidGroup groupify :named_group_member has_members :mongoid_issues has_members :mongoid_managers alias_method :issues, :mongoid_issues alias_method :managers, :mongoid_managers end describe Groupify::Mongoid do let!(:user) { MongoidUser.create! } let!(:group) { MongoidGroup.create! } let(:widget) { MongoidWidget.create! } let(:task) { MongoidTask.create! } let(:issue) { MongoidIssue.create! } let(:manager) { MongoidManager.create! } context 'when using groups' do it "members and groups are empty when initialized" do expect(user.groups).to be_empty expect(MongoidUser.new.groups).to be_empty expect(MongoidGroup.new.members).to be_empty expect(group.members).to be_empty end context "when adding" do it "adds a group to a member" do user.groups << group expect(user.groups).to include(group) expect(group.members).to include(user) expect(group.users).to include(user) end it "adds a member to a group" do expect(user.groups).to be_empty group.add user expect(user.groups).to include(group) expect(group.members).to include(user) end it "adds multiple members to a group" do group.add(user, task) expect(group.users).to include(user) expect(group.tasks).to include(task) users = [MongoidUser.create!, MongoidUser.create!] group.add(users) expect(group.users).to include(*users) end it "allows a group to also act as a member" do parent_group = MongoidGroup.create! child_group = MongoidGroup.create! parent_group.add(child_group) expect(parent_group.mongoid_groups).to include(child_group) end end it 'lists which member classes can belong to this group' do expect(group.class.member_classes).to include(MongoidUser, MongoidTask) expect(group.member_classes).to include(MongoidUser, MongoidTask) expect(MongoidProject.member_classes).to include(MongoidUser, MongoidTask, MongoidIssue) end it "finds members by group" do group.add user expect(MongoidUser.in_group(group).first).to eql(user) end it "finds the groups a member belongs to" do group.add user expect(MongoidGroup.with_member(user).first).to eq(group) end context 'when removing' do it "removes members from a group" do group.add user group.add widget group.users.delete(user) group.widgets.destroy(widget) expect(group.widgets).to_not include(widget) expect(group.users).to_not include(user) expect(widget.groups).to_not include(group) expect(user.groups).to_not include(group) end it "removes groups from a member" do group.add widget group.add user user.groups.delete(group) widget.groups.destroy(group) expect(group.widgets).to_not include(widget) expect(group.users).to_not include(user) expect(widget.groups).to_not include(group) expect(user.groups).to_not include(group) end it "removes the membership relation when a member is destroyed" do group.add user user.destroy expect(group).not_to be_destroyed expect(group.users).not_to include(user) end it "removes the membership relations when a group is destroyed" do group.add user group.add task group.destroy expect(user).not_to be_destroyed expect(user.reload.groups).to be_empty expect(task).not_to be_destroyed expect(task.reload.groups).to be_empty end end context 'when checking group membership' do it "members can check if they belong to any/all groups" do user.groups << group group2 = MongoidGroup.create! user.groups << group2 group3 = MongoidGroup.create! expect(user.groups).to include(group) expect(user.groups).to include(group2) expect(MongoidUser.in_group(group).first).to eql(user) expect(MongoidUser.in_group(group2).first).to eql(user) expect(user.in_group?(group)).to be true expect(MongoidUser.in_any_group(group).first).to eql(user) expect(MongoidUser.in_any_group(group3)).to be_empty expect(user.in_any_group?(group2, group3)).to be true expect(user.in_any_group?(group3)).to be false expect(MongoidUser.in_all_groups(group, group2).first).to eql(user) expect(MongoidUser.in_all_groups([group, group2]).first).to eql(user) expect(user.in_all_groups?(group, group2)).to be true expect(user.in_all_groups?(group, group3)).to be false end it "members can check if groups are shared" do user.groups << group widget.groups << group user2 = MongoidUser.create!(:groups => [group]) expect(user.shares_any_group?(widget)).to be true expect(MongoidWidget.shares_any_group(user).to_a).to include(widget) expect(MongoidUser.shares_any_group(widget).to_a).to include(user, user2) expect(user.shares_any_group?(user2)).to be true expect(MongoidUser.shares_any_group(user).to_a).to include(user2) end end context 'when merging' do it "moves the members from source to destination and destroys the source" do source = MongoidGroup.create! destination = MongoidProject.create! source.add(user) source.add(manager) destination.add(task) destination.merge!(source) expect(source.destroyed?).to be true expect(destination.users.to_a).to include(user) expect(destination.managers.to_a).to include(manager) expect(destination.tasks.to_a).to include(task) end it "moves membership types" do source = MongoidGroup.create! destination = MongoidProject.create! source.add(user) source.add(manager, as: 'manager') destination.merge!(source) expect(source.destroyed?).to be true expect(destination.users.as(:manager)).to include(manager) end it "fails to merge if the destination group cannot contain the source group's members" do source = MongoidProject.create! destination = MongoidGroup.create! source.add(issue) destination.add(user) # Issues cannot be members of a MongoidGroup expect {destination.merge!(source)}.to raise_error(ArgumentError) end it "merges incompatible groups as long as all the source members can be moved to the destination" do source = MongoidProject.create! destination = MongoidGroup.create! source.add(user) destination.add(task) expect {destination.merge!(source)}.to_not raise_error expect(source.destroyed?).to be true expect(destination.users.to_a).to include(user) expect(destination.tasks.to_a).to include(task) end end it "members can belong to many groups" do user.groups << group group2 = MongoidGroup.create! user.groups << group2 group3 = user.groups.create! user.save! group4 = MongoidGroup.create! expect(user.groups).to include(group, group2, group3) expect(MongoidUser.in_group(group).first).to eql(user) expect(MongoidUser.in_group(group2).first).to eql(user) expect(user.in_group?(group)).to be true expect(user.in_group?(group4)).to be false expect(MongoidUser.in_any_group(group, group4).first).to eql(user) expect(MongoidUser.in_any_group(group4)).to be_empty expect(user.in_any_group?(group2, group4)).to be true expect(user.in_any_group?(group4)).to be false expect(MongoidUser.in_all_groups(group, group2).first).to eql(user) expect(MongoidUser.in_all_groups([group, group3]).first).to eql(user) expect(MongoidUser.in_all_groups([group2, group4])).to be_empty expect(user.in_all_groups?(group, group3)).to be true expect(user.in_all_groups?(group, group4)).to be false expect(MongoidUser.in_only_groups(group, group2, group3).first).to eql(user) expect(MongoidUser.in_only_groups(group, group2, group3, group4)).to be_empty expect(MongoidUser.in_only_groups(group, group2)).to be_empty expect(user.in_only_groups?(group, group2, group3)).to be true expect(user.in_only_groups?(group, group2)).to be false expect(user.in_only_groups?(group, group2, group3, group4)).to be false end end context "when using membership types with groups" do it 'adds groups to a member with a specific membership type' do group.add(user, as: :admin) expect(user.groups).to include(group) expect(group.members).to include(user) expect(group.users).to include(user) expect(user.groups(:as => :admin)).to include(group) expect(user.groups.as(:admin)).to include(group) expect(group.members).to include(user) expect(group.users).to include(user) end it 'adds members to a group with specific membership types' do group.add(user, as: 'manager') group.add(widget) expect(user.groups).to include(group) expect(group.members).to include(user) expect(group.members.as(:manager)).to include(user) expect(group.users).to include(user) expect(user.groups(:as => :manager)).to include(group) expect(group.members).to include(user) expect(group.users).to include(user) end it "adds multiple members to a group with a specific membership type" do manager = MongoidUser.create! group.add(user, manager, as: :manager) expect(group.users.as(:manager)).to include(user, manager) expect(group.users(as: :manager)).to include(user, manager) end it "finds members by membership type" do group.add user, as: 'manager' expect(MongoidUser.as(:manager)).to include(user) end it "finds members by group with membership type" do group.add user, as: 'employee' manager = MongoidUser.create! group.add(user, manager, as: :manager) employees = MongoidUser.in_group(group).as('employee') expect(employees).to include(user) expect(employees).to_not include(manager) end it "finds the group a member belongs to with a membership type" do group.add user, as: MongoidManager user.groups.create! expect(MongoidGroup.with_member(user).as(MongoidManager).to_a).to eq([group]) end it "checks if members belong to any groups with a certain membership type" do group2 = MongoidGroup.create! group.add(user, as: 'employee') group2.add(user) expect(MongoidUser.in_any_group(group, group2).as('employee').first).to eql(user) end it "checks if members belong to all groups with a certain membership type" do group2 = MongoidGroup.create! group3 = MongoidGroup.create! group.add(user, as: 'employee') group2.add(user, as: 'employee') group3.add(user, as: 'contractor') expect(MongoidUser.in_all_groups(group, group2).as('employee').first).to eql(user) expect(MongoidUser.in_all_groups([group, group2]).as('employee').first).to eql(user) expect(MongoidUser.in_all_groups(group, group3).as('employee')).to be_empty expect(user.in_all_groups?(group, group2, group3)).to be true expect(user.in_all_groups?(group, group2, as: :employee)).to be true expect(user.in_all_groups?(group, group3, as: 'employee')).to be false end it "checks if members belong to only groups with a certain membership type" do group2 = MongoidGroup.create! group3 = MongoidGroup.create! group4 = MongoidGroup.create! group.add(user, as: 'employee') group2.add(user, as: 'employee') group3.add(user, as: 'contractor') group4.add(user, as: 'employee') expect(MongoidUser.in_only_groups(group, group2, group4).as('employee').first).to eql(user) expect(MongoidUser.in_only_groups([group, group2]).as('employee')).to be_empty expect(MongoidUser.in_only_groups(group, group2, group3, group4).as('employee')).to be_empty expect(user.in_only_groups?(group, group2, group3, group4)) expect(user.in_only_groups?(group, group2, group4, as: 'employee')).to be true expect(user.in_only_groups?(group, group2, as: 'employee')).to be false expect(user.in_only_groups?(group, group2, group3, group4, as: 'employee')).to be false end it "members can check if groups are shared with the same membership type" do user2 = MongoidUser.create! group.add(user, user2, widget, as: "Sub Group #1") expect(user.shares_any_group?(widget, as: "Sub Group #1")).to be true expect(MongoidWidget.shares_any_group(user).as("Sub Group #1").to_a).to include(widget) expect(MongoidUser.shares_any_group(widget).as("Sub Group #1").to_a).to include(user, user2) expect(user.shares_any_group?(user2, as: "Sub Group #1")).to be true expect(MongoidUser.shares_any_group(user).as("Sub Group #1").to_a).to include(user2) end context "when removing" do before(:each) do group.add user, as: 'employee' group.add user, as: 'manager' end it "removes all membership types when removing a member from a group" do group.add user group.users.destroy(user) expect(user.groups).to_not include(group) expect(user.groups.as('manager')).to_not include(group) expect(user.groups.as('employee')).to_not include(group) expect(group.users).to_not include(user) end it "removes a specific membership type from the member side" do user.groups.destroy(group, as: 'manager') expect(user.groups.as('manager')).to be_empty expect(user.groups.as('employee')).to include(group) expect(user.groups).to include(group) end it "removes a specific membership type from the group side" do group.users.delete(user, as: :manager) expect(user.groups.as('manager')).to be_empty expect(user.groups.as('employee')).to include(group) expect(user.groups).to include(group) end it "retains the member in the group if all membership types have been removed" do user.groups.delete(group, as: 'manager') user.groups.delete(group, as: 'employee') expect(user.groups).to include(group) end end end context 'when using named groups' do before(:each) do user.named_groups.concat :admin, :user, :poster user.save! end it "enforces uniqueness" do user.named_groups << :admin user.save! expect(user.named_groups.count{|g| g == :admin}).to eq(1) end it "queries named groups" do expect(user.named_groups).to include(:user, :admin) end it "removes named groups" do user.named_groups.delete(:admin, :poster) expect(user.named_groups).to include(:user) expect(user.named_groups).to_not include(:admin, :poster) user.named_groups.destroy(:user) expect(user.named_groups).to be_empty end it "removes all named groups" do user.named_groups.destroy_all expect(user.named_groups).to be_empty end it "checks if a member belongs to one named group" do expect(user.in_named_group?(:admin)).to be true expect(MongoidUser.in_named_group(:admin).first).to eql(user) end it "checks if a member belongs to any named group" do expect(user.in_any_named_group?(:admin, :user, :test)).to be true expect(user.in_any_named_group?(:foo, :bar)).to be false expect(MongoidUser.in_any_named_group(:admin, :test).first).to eql(user) expect(MongoidUser.in_any_named_group(:test)).to be_empty end it "checks if a member belongs to all named groups" do expect(user.in_all_named_groups?(:admin, :user)).to be true expect(user.in_all_named_groups?(:admin, :user, :test)).to be false expect(MongoidUser.in_all_named_groups(:admin, :user).first).to eql(user) end it "checks if a member belongs to only certain named groups" do expect(user.in_only_named_groups?(:admin, :user, :poster)).to be true expect(user.in_only_named_groups?(:admin, :user, :poster, :foo)).to be false expect(user.in_only_named_groups?(:admin, :user)).to be false expect(user.in_only_named_groups?(:admin, :user, :test)).to be false expect(MongoidUser.in_only_named_groups(:admin, :user, :poster).first).to eql(user) expect(MongoidUser.in_only_named_groups(:admin, :user, :poster, :foo)).to be_empty expect(MongoidUser.in_only_named_groups(:admin)).to be_empty end it "checks if named groups are shared" do user2 = MongoidUser.create!(:named_groups => [:admin]) expect(user.shares_any_named_group?(user2)).to be true expect(MongoidUser.shares_any_named_group(user).to_a).to include(user2) end context 'and using membership types with named groups' do before(:each) do user.named_groups.concat :team1, :team2, as: 'employee' user.named_groups.push :team3, as: 'manager' user.named_groups.push :team1, as: 'developer' end it "queries named groups, filtering by membership type" do expect(user.named_groups).to include(:team1, :team2, :team3) expect(user.named_groups.as('manager')).to eq([:team3]) end it "enforces uniqueness of named groups" do user.named_groups << :team1 expect(user.named_groups.count{|g| g == :team1}).to eq(1) expect(user.named_groups.as('employee').count{|g| g == :team1}).to eq(1) end it "enforces uniqueness of group name and membership type for group memberships" do user.named_groups.push :team1, as: 'employee' expect(user.named_groups.count{|g| g == :team1}).to eq(1) expect(user.named_groups.as('employee').count{|g| g == :team1}).to eq(1) end it "checks if a member belongs to one named group with a certain membership type" do expect(user.in_named_group?(:team1, as: 'employee')).to be true expect(MongoidUser.in_named_group(:team1).as('employee').first).to eql(user) end it "checks if a member belongs to any named group with a certain membership type" do expect(user.in_any_named_group?(:team1, :team3, as: 'employee')).to be true expect(MongoidUser.in_any_named_group(:team2, :team3).as('manager').first).to eql(user) end it "checks if a member belongs to all named groups with a certain membership type" do expect(user.in_all_named_groups?(:team1, :team2, as: 'employee')).to be true expect(user.in_all_named_groups?(:team1, :team3, as: 'employee')).to be false expect(MongoidUser.in_all_named_groups(:team1, :team2).as('employee').first).to eql(user) end it "checks if a member belongs to only certain named groups with a certain membership type" do expect(user.in_only_named_groups?(:team1, :team2, as: 'employee')).to be true expect(user.in_only_named_groups?(:team1, as: 'employee')).to be false expect(user.in_only_named_groups?(:team1, :team3, as: 'employee')).to be false expect(user.in_only_named_groups?(:foo, as: 'employee')).to be false expect(MongoidUser.in_only_named_groups(:team1, :team2).as('employee').first).to eql(user) expect(MongoidUser.in_only_named_groups(:team1).as('employee')).to be_empty expect(MongoidUser.in_only_named_groups(:foo).as('employee')).to be_empty end it "checks if a member shares any named groups with a certain membership type" do project = MongoidProject.create!(:named_groups => [:team3]) expect(user.shares_any_named_group?(project, as: 'manager')).to be true expect(MongoidUser.shares_any_named_group(project).as('manager').to_a).to include(user) end it "removes named groups with a certain membership type" do user.named_groups.delete(:team1, as: :employee) expect(user.named_groups.as(:employee)).to include(:team2) expect(user.named_groups.as(:employee)).to_not include(:team1) expect(user.named_groups.as(:developer)).to include(:team1) expect(user.named_groups).to include(:team1) end it "removes all named group memberships if membership type is not specified" do user.named_groups.destroy(:team1) expect(user.named_groups).to_not include(:team1) expect(user.named_groups.as(:employee)).to_not include(:team1) expect(user.named_groups.as(:developer)).to_not include(:team1) expect(user.named_groups.as(:employee)).to include(:team2) end end end end