expertiza/expertiza

View on GitHub
spec/models/collusion_cycle_spec.rb

Summary

Maintainability
C
1 day
Test Coverage
describe CollusionCycle do
  #
  # assignment participant -----
  #    ^                       |
  #    |                       V
  #    ------------- current reviewer (ap)
  #
  subject(:cycle) { CollusionCycle.new }
  let(:assignment) { build(:assignment, id: 1) }
  let(:questionnaire) { build(:questionnaire, id: 1) }
  (1..4).each do |i|
    let("team#{i}".to_sym) { build(:assignment_team, id: i, name: "team#{i}", assignment: assignment) }
    let("participant#{i}".to_sym) { build(:participant, id: i, user: build(:student, name: "stu#{i}"), assignment: assignment) }
  end
  let(:response_1_2) { build(:response, id: 1) }
  let(:response_2_1) { build(:response, id: 2) }
  let(:response_2_3) { build(:response, id: 3) }
  let(:response_3_1) { build(:response, id: 4) }
  let(:response_3_4) { build(:response, id: 5) }
  let(:response_4_1) { build(:response, id: 6) }
  let(:response_map_team_1_2) do
    build(:review_response_map,
          id: 1,
          reviewee_id: team1.id,
          reviewer_id: participant2.id,
          response: [response_1_2],
          assignment: assignment)
  end
  let(:response_map_team_2_1) do
    build(:review_response_map,
          id: 2,
          reviewee_id: team2.id,
          reviewer_id: participant1.id,
          response: [response_2_1],
          assignment: assignment)
  end
  let(:response_map_team_2_3) do
    build(:review_response_map,
          id: 3,
          reviewee_id: team2.id,
          reviewer_id: participant3.id,
          response: [response_2_3],
          assignment: assignment)
  end
  let(:response_map_team_3_1) do
    build(:review_response_map,
          id: 4,
          reviewee_id: team3.id,
          reviewer_id: participant1.id,
          response: [response_3_1],
          assignment: assignment)
  end
  let(:response_map_team_3_4) do
    build(:review_response_map,
          id: 5,
          reviewee_id: team3.id,
          reviewer_id: participant4.id,
          response: [response_3_4],
          assignment: assignment)
  end
  let(:response_map_team_4_1) do
    build(:review_response_map,
          id: 6,
          reviewee_id: team4.id,
          reviewer_id: participant1.id,
          response: [response_4_1],
          assignment: assignment)
  end

  before(:each) do
    allow(participant1).to receive(:team).and_return(team1)
    allow(participant2).to receive(:team).and_return(team2)
    allow(participant3).to receive(:team).and_return(team3)
    allow(participant4).to receive(:team).and_return(team4)
    allow(participant1).to receive(:review_score).and_return(90)
    allow(participant2).to receive(:review_score).and_return(70)
    allow(participant3).to receive(:review_score).and_return(99)
    allow(participant4).to receive(:review_score).and_return(100)
    allow(AssignmentParticipant).to receive(:find).with(1).and_return(participant1)
    allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
    allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant3)
    allow(AssignmentParticipant).to receive(:find).with(4).and_return(participant4)
    allow(response_1_2).to receive(:total_score).and_return(90)
    allow(response_2_1).to receive(:total_score).and_return(95)
    allow(response_2_3).to receive(:total_score).and_return(82)
    allow(response_3_1).to receive(:total_score).and_return(97)
    allow(response_3_4).to receive(:total_score).and_return(80)
    allow(response_4_1).to receive(:total_score).and_return(0)
  end

  describe '#two_node_cycles' do
    context 'when the reviewers of current reviewer (ap) does not include current assignment participant' do
      it 'skips this reviewer (ap) and returns corresponding collusion cycles' do
        # Sets up stubs for test
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([])

        # Tests if current reviewer does not include current assignment participant
        expect(cycle.two_node_cycles(participant1)).to eq([])
      end
    end

    context 'when the reviewers of current reviewer (ap) includes current assignment participant' do
      # This before each function is used to extract out re-appearing code used in two_node_cycle tests
      # More specifically, it is used to extract out the common code used to
      # create a relationship between two revewing participants
      before(:each) do
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([response_map_team_2_1])
        allow(AssignmentParticipant).to receive(:find).with(1).and_return(participant1)
      end

      context 'when current assignment participant was not reviewed by current reviewer (ap)' do
        it 'skips current reviewer (ap) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(nil)

          # Tests if current assignment participant was not reviewed by current reviewer
          expect(cycle.two_node_cycles(participant1)).to eq([])
        end
      end

      context 'when current assignment participant was reviewed by current reviewer (ap)' do
        it 'inserts related information into collusion cycles and returns results' do
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team1.id, reviewer_id: participant2.id).and_return([response_map_team_1_2])
          allow(Response).to receive(:where).with(map_id: [response_map_team_1_2]).and_return([response_1_2])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team2.id, reviewer_id: participant1.id).and_return([response_map_team_2_1])
          allow(Response).to receive(:where).with(map_id: [response_map_team_2_1]).and_return([response_2_1])

          # Tests if current assignment participant was reviewed by current reviewer and insert related information into collusion cycles array
          expect(cycle.two_node_cycles(participant1)[0][0]).to eq([participant1, 90])
        end
      end

      context 'when current reviewer (ap) was not reviewed by current assignment participant' do
        it 'skips current reviewer (ap) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant1).and_return(nil)

          # Tests if reviewer was not reviewed by assignment participant
          expect(cycle.two_node_cycles(participant1)).to eq([])
        end
      end

      context 'when current reviewer (ap) was reviewed by current assignment participant' do
        it 'inserts related information into collusion cycles and returns results' do
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team1.id, reviewer_id: participant2.id).and_return([response_map_team_1_2])
          allow(Response).to receive(:where).with(map_id: [response_map_team_1_2]).and_return([response_1_2])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team2.id, reviewer_id: participant1.id).and_return([response_map_team_2_1])
          allow(Response).to receive(:where).with(map_id: [response_map_team_2_1]).and_return([response_2_1])

          # Tests if reviewer was reviewed by assignment participant and inserted related information into coluusion cycle arr
          expect(cycle.two_node_cycles(participant1)).to eq([[[participant1, 90], [participant2, 95]]])
        end
      end
    end
  end

  #
  # assignment participant -----
  #    ^                       |
  #    |                       v
  # current reviewee (ap1) <-- current reviewer (ap2)
  #
  describe '#three_node_cycles' do
    context 'when the reviewers of current reviewer (ap2) does not include current assignment participant' do
      it 'skips this reviewer (ap2) and returns corresponding collusion cycles' do
        # Sets up stubs for test
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([response_map_team_2_3])
        allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant3)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team3.id).and_return([])

        # Tests if current reviewer does not include current assignment participant
        expect(cycle.three_node_cycles(participant1)).to eq([])
      end
    end

    context 'when the reviewers of current reviewer (ap2) includes current assignment participant' do
      # This before-each function is used to extract out re-appearing code used in three_node_cycle tests
      # More specifically, it is used to extract out the common code used to
      # create a relationship between three revewing participants.
      before(:each) do
        # Sets up stubs for test
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([response_map_team_2_3])
        allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant3)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team3.id).and_return([response_map_team_3_1])
        allow(AssignmentParticipant).to receive(:find).with(1).and_return(participant1)
      end
      context 'when current assignment participant was not reviewed by current reviewee (ap1)' do
        it 'skips current reviewer (ap2) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(nil)
          # Tests if current assignment participant was not reviewed by current reviewer
          expect(cycle.three_node_cycles(participant1)).to eq([])
        end
      end

      context 'when a full, three participant relationship has been constructed' do
        before(:each) do
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team1.id, reviewer_id: participant2.id).and_return([response_map_team_1_2])
          allow(Response).to receive(:where).with(map_id: [response_map_team_1_2]).and_return([response_1_2])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team2.id, reviewer_id: participant3.id).and_return([response_map_team_2_3])
          allow(Response).to receive(:where).with(map_id: [response_map_team_2_3]).and_return([response_2_3])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team3.id, reviewer_id: participant1.id).and_return([response_map_team_3_1])
          allow(Response).to receive(:where).with(map_id: [response_map_team_3_1]).and_return([response_3_1])
        end
        context 'when current assignment participant was reviewed by current reviewee (ap1)' do
          it 'inserts related information into collusion cycles and returns results' do
            # Sets up stubs for test
            # Tests if current assignment participant was reviewed by current reviewer and insert related information into collusion cycles array
            expect(cycle.three_node_cycles(participant1)[0][0]).to eq([participant1, 90])
          end
        end

        context 'when current reviewee (ap1) was reviewed by current reviewer (ap2)' do
          it 'inserts related information into collusion cycles and returns results' do
            expect(cycle.three_node_cycles(participant1)).to eq([[[participant1, 90], [participant2, 82], [participant3, 97]]])
          end
        end

        context 'when current reviewer (ap2) was reviewed by current assignment participant' do
          it 'inserts related information into collusion cycles and returns results' do
            # Tests if reviewer (ap2) was reviewed by assignment participant and inserted related information into collusion cycle arr
            expect(cycle.three_node_cycles(participant1)).to eq([[[participant1, 90], [participant2, 82], [participant3, 97]]])
          end
        end
      end

      context 'when current reviewer (ap2) was not reviewed by current assignment participant' do
        it 'skips current reviewer (ap2) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant1).and_return(nil)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(response_2_3)
          expect(cycle.three_node_cycles(participant1)).to eq([])
        end
      end

      context 'when current reviewee (ap1) was not reviewed by current reviewer (ap2)' do
        it 'skips current reviewer (ap2) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant1).and_return(response_3_1)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(nil)
          expect(cycle.three_node_cycles(participant1)).to eq([])
        end
      end
    end
  end

  #
  #             assignment participant --> current reviewer (ap3)
  #                                ^       |
  #                                |       v
  # reviewee of current reviewee (ap1) <--current reviewee (ap2)
  #
  describe '#four_node_cycles' do
    context 'when the reviewers of current reviewer (ap3) does not include current assignment participant' do
      it 'skips this reviewer (ap3) and returns corresponding collusion cycles' do
        # Sets up stubs for test
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([response_map_team_2_3])
        allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant3)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team3.id).and_return([response_map_team_3_4])
        allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant4)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team4.id).and_return([])

        # Tests if current reviewer does not include current assignment participant
        expect(cycle.four_node_cycles(participant1)).to eq([])
      end
    end

    context 'when the reviewers of current reviewer (ap3) includes current assignment participant' do
      # This before-each function is used to extract out re-appearing code used in four_node_cycle tests
      # More specifically, it is used to extract out the common code used to
      # create a relationship between four revewing participants
      before(:each) do
        # Sets up stubs for test
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team1.id).and_return([response_map_team_1_2])
        allow(AssignmentParticipant).to receive(:find).with(2).and_return(participant2)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team2.id).and_return([response_map_team_2_3])
        allow(AssignmentParticipant).to receive(:find).with(3).and_return(participant3)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team3.id).and_return([response_map_team_3_4])
        allow(AssignmentParticipant).to receive(:find).with(4).and_return(participant4)
        allow(ReviewResponseMap).to receive(:where).with('reviewee_id = ?', team4.id).and_return([response_map_team_4_1])
        allow(AssignmentParticipant).to receive(:find).with(1).and_return(participant1)
      end
      context 'when a full, four participant relationship has been constructed' do
        before(:each) do
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team1.id, reviewer_id: participant2.id).and_return([response_map_team_1_2])
          allow(Response).to receive(:where).with(map_id: [response_map_team_1_2]).and_return([response_1_2])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team2.id, reviewer_id: participant3.id).and_return([response_map_team_2_3])
          allow(Response).to receive(:where).with(map_id: [response_map_team_2_3]).and_return([response_2_3])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team3.id, reviewer_id: participant4.id).and_return([response_map_team_3_4])
          allow(Response).to receive(:where).with(map_id: [response_map_team_3_4]).and_return([response_3_4])
          allow(ReviewResponseMap).to receive(:where).with(reviewee_id: team4.id, reviewer_id: participant1.id).and_return([response_map_team_4_1])
          allow(Response).to receive(:where).with(map_id: [response_map_team_4_1]).and_return([response_4_1])
        end
        context 'when current assignment participant was reviewed by the reviewee of current reviewee (ap1)' do
          it 'inserts related information into collusion cycles and returns results' do
            # Tests if current assignment participant was reviewed by current
            # reviewer and insert related information into collusion cycles array
            expect(cycle.four_node_cycles(participant1)[0][0]).to eq([participant1, 90])
          end
        end

        context 'when the reviewee of current reviewee (ap1) was reviewed by current reviewee (ap2)' do
          it 'inserts related information into collusion cycles and returns results' do
            allow(ReviewResponseMap).to receive(:where).with(any_args).and_return([])
            # Tests if reviewer was not reviewed by assignment participant
            expect(cycle.four_node_cycles(participant1)).to eq([])
          end
        end

        context 'when current reviewee (ap2) was reviewed by current reviewer (ap3)' do
          it 'inserts related information into collusion cycles and returns results' do
            # Tests if reviewer was reviewed by assignment participant and inserted related information into coluusion cycle arr
            expect(cycle.four_node_cycles(participant1)).to eq([[[participant1, 90], [participant2, 82], [participant3, 80], [participant4, 0]]])
          end
        end

        context 'when current reviewer (ap3) was reviewed by current assignment participant' do
          it 'inserts related information into collusion cycles and returns results' do
            # Tests if reviewer was reviewed by assignment participant and inserted related information into coluusion cycle arr
            expect(cycle.four_node_cycles(participant1)).to eq([[[participant1, 90], [participant2, 82], [participant3, 80], [participant4, 0]]])
          end
        end
      end

      context 'when current assignment participant was not reviewed by the reviewee of current reviewee (ap1)' do
        it 'skips current reviewer (ap3) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(nil)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(response_2_3)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant4).and_return(response_3_4)
          allow(participant4).to receive(:reviews_by_reviewer).with(participant1).and_return(response_4_1)

          # Tests if current assignment participant was not reviewed by current reviewer
          expect(cycle.four_node_cycles(participant1)).to eq([])
        end
      end

      context 'when the reviewee of current reviewee (ap1) was not reviewed by current reviewee (ap2)' do
        it 'skips current reviewer (ap3) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(nil)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant4).and_return(response_3_4)
          allow(participant4).to receive(:reviews_by_reviewer).with(participant1).and_return(response_4_1)
          expect(cycle.four_node_cycles(participant1)).to eq([])
        end
      end

      context 'when current reviewee (ap2) was not reviewed by current reviewer (ap3)' do
        it 'skips current reviewer (ap3) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(response_2_3)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant4).and_return(nil)
          allow(participant4).to receive(:reviews_by_reviewer).with(participant1).and_return(response_4_1)

          # Tests if current assignment participant was not reviewed by current reviewer
          expect(cycle.four_node_cycles(participant1)).to eq([])
        end
      end

      context 'when current reviewer (ap3) was not reviewed by current assignment participant' do
        it 'skips current reviewer (ap3) and returns corresponding collusion cycles' do
          allow(participant1).to receive(:reviews_by_reviewer).with(participant2).and_return(response_1_2)
          allow(participant2).to receive(:reviews_by_reviewer).with(participant3).and_return(response_2_3)
          allow(participant3).to receive(:reviews_by_reviewer).with(participant4).and_return(response_3_4)
          allow(participant4).to receive(:reviews_by_reviewer).with(participant1).and_return(nil)
          # Tests if reviewer was not reviewed by assignment participant
          expect(cycle.four_node_cycles(participant1)).to eq([])
        end
      end
    end
  end

  describe '#cycle_similarity_score' do
    context 'when collusion cycle has been calculated, verify the similarity score' do
      it 'returns similarity score based on inputted 2 node cycle' do
        c = [[participant1, 90], [participant2, 70]]
        expect(cycle.cycle_similarity_score(c)).to eql(20.0)
      end
      it 'returns similarity score based on inputted 3 node cycle' do
        c = [[participant1, 90], [participant2, 60], [participant2, 30]]
        expect(cycle.cycle_similarity_score(c)).to eql(40.0)
      end
      it 'returns similarity score based on inputted 4 node cycle' do
        c = [[participant1, 80], [participant1, 40], [participant1, 40], [participant1, 0]]
        expect(cycle.cycle_similarity_score(c)).to eql(40.0)
      end
    end
  end

  describe '#cycle_deviation_score' do
    context 'when collusion cycle has been calculated, verify the deviation score' do
      it 'returns cycle deviation score based on inputted 2 node cycle' do
        c = [[participant1, 91], [participant2, 71]]
        expect(cycle.cycle_deviation_score(c)).to eql(1.0)
      end
      it 'returns cycle deviation score based on inputted 3 node cycle' do
        c = [[participant1, 92], [participant2, 72], [participant3, 97]]
        expect(cycle.cycle_deviation_score(c)).to eql(2.0)
      end
      it 'returns cycle deviation score based on inputted 4 node cycle' do
        c = [[participant1, 91], [participant2, 71], [participant3, 100], [participant4, 99]]
        expect(cycle.cycle_deviation_score(c)).to eql(1.0)
      end
    end
  end
end