Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
canvas
/
spec
/
lib
/
microsoft_sync
/
Filename :
partial_membership_diff_spec.rb
back
Copy
# frozen_string_literal: true # # Copyright (C) 2021 - present 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/>. describe MicrosoftSync::PartialMembershipDiff do # We test this by adding users for every possible combination: # Mapping of change type (m, o) and enrollment state (X=none, M, O) to actions to take: # 1. m-X remove member # 2. m-M add member # 3. m-O ??? do nothing (add member would also be fine but seems unnecessary) # this happens when either: # was MO and M was removed # was O, M added and removed # was M, M removed, O added but not seen yet # was X, M added and removed, O added but not seen yet # 4. m-MO add member # 5. o-X remove member, remove owner # 6. o-M remove owner, add member (future remove-remove-add DANCE) # 7. o-O add member, add owner # 8. o-MO add member, add owner # 9. mo-X remove member, remove owner # 10. mo-M remove owner, add member (future remove-remove-add DANCE) # 11. mo-O add member, add owner # 12. mo-MO add member, add owner # # subject { create_diff(users_to_msft_role_types, local_members, member_mappings) } let(:users_to_msft_role_types) do { 101 => %w[member], 102 => %w[member], 103 => %w[member], 104 => %w[member], 105 => %w[owner], 106 => %w[owner], 107 => %w[owner], 108 => %w[owner], 109 => %w[member owner], 110 => %w[member owner], 111 => %w[member owner], 112 => %w[member owner], } end let(:local_members) do [ # 101 m_X none [102, student_enrollment_type], # m_M [103, teacher_enrollment_type], # m_O [104, student_enrollment_type], # m_MO [104, teacher_enrollment_type], # m_MO # 105 o_X none [106, student_enrollment_type], # o_M [107, teacher_enrollment_type], # o_O [108, student_enrollment_type], # o_MO [108, teacher_enrollment_type], # o_MO # 109 mo_X none [110, student_enrollment_type], # mo_M [111, teacher_enrollment_type], # mo_O [112, student_enrollment_type], # mo_MO [112, teacher_enrollment_type], # mo_MO ] end let(:student_enrollment_type) { "StudentEnrollment" } let(:teacher_enrollment_type) { "TeacherEnrollment" } let(:slice_size) { 20 } let(:member_mappings) do { "m_X" => 101, "m_M" => 102, "m_O" => 103, "m_MO" => 104, "o_X" => 105, "o_M" => 106, "o_O" => 107, "o_MO" => 108, "mo_X" => 109, "mo_M" => 110, "mo_O" => 111, "mo_MO" => 112, } end let(:actions_by_aad) { actions_by_aad_for_diff(subject) } def actions_by_aad_for_diff(diff) actions = Hash.new { |hash, key| hash[key] = [] } diff.additions_in_slices_of(slice_size) do |additions| additions[:owners]&.each { |aad| actions[aad] << :add_owner } additions[:members]&.each { |aad| actions[aad] << :add_member } end diff.removals_in_slices_of(slice_size) do |removals| removals[:owners]&.each { |aad| actions[aad] << :remove_owner } removals[:members]&.each { |aad| actions[aad] << :remove_member } end actions end def create_diff(users_to_msft_role_types, aads_and_enrollment_types, mappings) diff = described_class.new(users_to_msft_role_types) aads_and_enrollment_types.each do |aad_id, enrollment_type| diff.set_local_member(aad_id, enrollment_type) end mappings.each do |aad_id, user_id| diff.set_member_mapping(user_id, aad_id) end diff end expected_actions = { m_X: %i[remove_member], m_M: %i[add_member], m_O: [], m_MO: %i[add_member], o_X: %i[remove_member remove_owner], o_M: %i[remove_owner add_member], o_O: %i[add_member add_owner], o_MO: %i[add_member add_owner], mo_X: %i[remove_member remove_owner], mo_M: %i[remove_owner add_member], mo_O: %i[add_member add_owner], mo_MO: %i[add_member add_owner], }.transform_values(&:freeze).freeze [20, 4].each do |slice_len| context "with a slice size of #{slice_len}" do let(:slice_size) { slice_len } expected_actions.each do |aad, actions| context "for a user with MSFT role type and enrollment types '#{aad}'" do it "executes the actions #{actions.inspect}" do expect(actions_by_aad[aad.to_s]).to match_array(expected_actions[aad]) end end end end end it "dedupes aads" do subject.set_member_mapping(111, "mo_MO") expect(actions_by_aad["mo_O"]).to_not be_present expect(actions_by_aad["mo_MO"]).to match_array(%i[add_member add_owner]) end context "when mappings are missing" do it "does not suggest any changes for those users" do mappings2 = member_mappings.except("m_X", "o_M") diff2 = create_diff(users_to_msft_role_types, local_members, mappings2) actions2 = actions_by_aad_for_diff(diff2) expect(actions2.keys).to match_array(%w[m_M m_MO o_X o_O o_MO mo_X mo_M mo_O mo_MO]) expect(actions2).to eq(actions_by_aad.except("m_X", "o_M")) end end describe "enrollment type classification" do %w[TeacherEnrollment TaEnrollment DesignerEnrollment].each do |owner_enrollment| context "when an enrollment is of type #{owner_enrollment}" do let(:teacher_enrollment_type) { owner_enrollment } it "classifies it as an owner" do expect(actions_by_aad["o_O"]).to match_array(expected_actions[:o_O]) end end end %w[StudentEnrollment ObserverEnrollment].each do |member_enrollment| context "when an enrollment is of type #{member_enrollment}" do let(:student_enrollment_type) { member_enrollment } it "classifies it as an member" do expect(actions_by_aad["o_M"]).to match_array(expected_actions[:o_M]) end end end end context "slices" do let(:slice_size) { 3 } let(:removals_slices) do [].tap { |actions| subject.removals_in_slices_of(slice_size) { |slice| actions << slice } } end let(:additions_slices) do [].tap { |actions| subject.additions_in_slices_of(slice_size) { |slice| actions << slice } } end before { allow(MicrosoftSync::MembershipDiff).to receive(:in_slices_of).and_call_original } it "batches additions in slices, owners first" do expect(additions_slices.map { |slice| slice.transform_values(&:count) }).to eq([ { members: 0, owners: 3 }, { members: 2, owners: 1 }, { members: 3 }, { members: 3 }, ]) end it "batches removals in slices, owners first" do expect(removals_slices.map { |slice| slice.transform_values(&:count) }).to eq([ { members: 0, owners: 3 }, { members: 2, owners: 1 }, { members: 1 }, ]) end it "uses MembershipDiff.in_slices_of for additions" do additions_slices expect(MicrosoftSync::MembershipDiff).to have_received(:in_slices_of).once end it "uses MembershipDiff.in_slices_of for removals" do removals_slices expect(MicrosoftSync::MembershipDiff).to have_received(:in_slices_of).once end end describe "#log_all_actions" do it "logs user ids, aad ids, change types, enrollment types, and actions" do logs = [] expect(Rails.logger).to receive(:info).at_least(:once) do |line| logs << line end subject.log_all_actions logs = logs.filter_map { |l| l.dup.gsub!(/^MicrosoftSync::PartialMembershipDiff: /, "") }.sort expect(logs.join("\n")).to eq([ 'User 101 (m_X): change ["member"], enrolls [] -> [:remove_member]', 'User 102 (m_M): change ["member"], enrolls ["StudentEnrollment"] -> [:add_member]', 'User 103 (m_O): change ["member"], enrolls ["TeacherEnrollment"] -> []', 'User 104 (m_MO): change ["member"], enrolls ["StudentEnrollment", "TeacherEnrollment"] -> [:add_member]', 'User 105 (o_X): change ["owner"], enrolls [] -> [:remove_member, :remove_owner]', 'User 106 (o_M): change ["owner"], enrolls ["StudentEnrollment"] -> [:add_member, :remove_owner]', 'User 107 (o_O): change ["owner"], enrolls ["TeacherEnrollment"] -> [:add_member, :add_owner]', 'User 108 (o_MO): change ["owner"], enrolls ["StudentEnrollment", "TeacherEnrollment"] -> [:add_member, :add_owner]', 'User 109 (mo_X): change ["member", "owner"], enrolls [] -> [:remove_member, :remove_owner]', 'User 110 (mo_M): change ["member", "owner"], enrolls ["StudentEnrollment"] -> [:add_member, :remove_owner]', 'User 111 (mo_O): change ["member", "owner"], enrolls ["TeacherEnrollment"] -> [:add_member, :add_owner]', 'User 112 (mo_MO): change ["member", "owner"], enrolls ["StudentEnrollment", "TeacherEnrollment"] -> [:add_member, :add_owner]', ].join("\n")) end end end