require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'blueprints', 'helper'))
require 'shoulda'

class AssignmentTest < ActiveSupport::TestCase

  context 'An Assignment' do
    setup do
      @assignment = Assignment.make(:group_name_autogenerated => false)
    end

    context 'as a noteable' do
      should 'display for note without seeing an exception' do
        assignment = Assignment.make
        assert_nothing_raised do
          assignment.display_for_note
        end
      end
    end # end noteable context

    context 'with a student in a group with a marked submission' do
      setup do
        @membership = StudentMembership.make(:grouping => Grouping.make(:assignment => @assignment),:membership_status => StudentMembership::STATUSES[:accepted])
        sub = Submission.make(:grouping => @membership.grouping)
        @result = sub.get_latest_result

        @sum = 0
        [2,2.7,2.2,2].each do |weight|
          Mark.make({:mark => 4, :result => @result, :markable => RubricCriterion.make({:assignment => @assignment,:weight => weight})})
          @sum += weight
        end
        @total = @sum * 4
      end

      should 'return the correct total mark for rubric criteria' do
        assert_equal(@total, @assignment.total_mark)
      end

      should 'return the correct total criteria weight' do
        assert_equal(@sum, @assignment.total_criteria_weight)
      end

      # Test if assignments can fetch the group for a user
      should 'return the correct group for a given student' do
        assert_equal @membership.grouping.group, @assignment.group_by(@membership.user).group
      end
    end

    should 'be able to create groupings when students work alone' do
      (1..5).each do
        Student.make
      end

      assert_equal(0, @assignment.groupings.count)
      assert @assignment.create_groupings_when_students_work_alone
      assert_equal(5, @assignment.groupings.count)
    end

    context "with some groupings with students and ta's assigned " do
      setup do
        (1..5).each do
          grouping = Grouping.make(:assignment => @assignment)
          (1..3).each do
            StudentMembership.make({:grouping => grouping, :membership_status => StudentMembership::STATUSES[:accepted]})
          end
          TaMembership.make({:grouping => grouping, :membership_status => StudentMembership::STATUSES[:accepted]})
        end
      end

      should "be able to have it's groupings cloned correctly" do
        clone = Assignment.make({:group_min => 1, :group_max => 1})
        number = StudentMembership.all.size + TaMembership.all.size
        clone.clone_groupings_from(@assignment.id)
        assert_equal(@assignment.group_min, clone.group_min)
        assert_equal(@assignment.group_max, clone.group_max)
        assert_equal(@assignment.groupings.size, clone.groupings.size)
        # Since we clear between each test, there should be twice as much as previously
        assert_equal(2 * number, StudentMembership.all.size + TaMembership.all.size)
      end
    end

    # One student in a grouping is hidden, so that membership should
    # not be cloned
    context 'with a group with 3 accepted students' do
      setup do
        @grouping = Grouping.make(:assignment => @assignment)
        @members = []
        (1..3).each do
          @members.push StudentMembership.make({:membership_status => StudentMembership::STATUSES[:accepted],:grouping => @grouping})
        end
        @source = @assignment
        @group =  @grouping.group
      end

      context 'with another fresh assignment' do
        setup do
          @target = Assignment.make({:group_min => 1, :group_max => 1})
        end

        should 'clone all three members if none are hidden' do
          # clone the groupings
          @target.clone_groupings_from(@source.id)
          # and let's make sure that the memberships were cloned
          (0..2).each do |index|
            assert @members[index].user.has_accepted_grouping_for?(@target.id)
          end
          @group.reload
          assert !@group.groupings.find_by_assignment_id(@target.id).nil?
        end

        should 'ignore a blocked student during cloning' do
          student = @members[0].user
          # hide the student
          student.hidden = true
          student.save
          # clone the groupings
          @target.clone_groupings_from(@source.id)
          # make sure the membership wasn't created for the hidden
          # student
          assert !student.has_accepted_grouping_for?(@target.id)
          # and let's make sure that the other memberships were cloned
          assert @members[1].user.has_accepted_grouping_for?(@target.id)
          assert @members[2].user.has_accepted_grouping_for?(@target.id)
          @group.reload
          assert !@group.groupings.find_by_assignment_id(@target.id).nil?
        end

        should 'ignore two blocked students during cloning' do
          # hide the students
          @members[0].user.hidden = true
          @members[0].user.save
          @members[1].user.hidden = true
          @members[1].user.save
          # clone the groupings
          @target.clone_groupings_from(@source.id)
          # make sure the membership wasn't created for the hidden
          # student
          assert !@members[0].user.has_accepted_grouping_for?(@target.id)
          assert !@members[1].user.has_accepted_grouping_for?(@target.id)
          # and let's make sure that the other membership was cloned
          assert @members[2].user.has_accepted_grouping_for?(@target.id)
          # and that the proper grouping was created
          @group.reload
          assert !@group.groupings.find_by_assignment_id(@target.id).nil?
        end

        should 'ignore grouping if all students hidden' do
          # hide the students
          (0..2).each do |index|
            @members[index].user.hidden = true
            @members[index].user.save
          end

          # Get the Group that these students belong to for assignment_1
          assert @members[0].user.has_accepted_grouping_for?(@source.id)
          # clone the groupings
          @target.clone_groupings_from(@source.id)
          # make sure the membership wasn't created for the hidden
          # student
          (0..2).each do |index|
            assert !@members[index].user.has_accepted_grouping_for?(@target.id)
          end
          # and let's make sure that the grouping wasn't cloned
          @group.reload
          assert @group.groupings.find_by_assignment_id(@target.id).nil?
        end
      end

      context 'with an assignment with other groupings' do
        setup do
          @target = Assignment.make({:group_min => 1, :group_max => 1})
          @target.create_groupings_when_students_work_alone
        end
        should 'destroy all previous groupings if cloning was successful' do
          old_groupings = @target.groupings
          @target.clone_groupings_from(@source.id)
          @target.reload
          old_groupings.each do |old_grouping|
            assert !@target.groupings.include?(old_grouping)
          end
        end
      end

      context 'an assignment with external commits only and previous groups' do
        setup do
          @assignment.allow_web_submits = false
          @assignment.save
          @target = Assignment.make({:allow_web_submits => false, :group_min => 1, :group_max => 1})
          @target.create_groupings_when_students_work_alone
          # And for this test, let's make sure all groupings cloned have admin approval
          @assignment.groupings.each do |grouping|
            grouping.admin_approved = true
            grouping.save
          end
          assert @assignment.groupings.size > 0
        end

        should 'ensure that all students have appropriate permissions on the cloned groupings' do
          @target.clone_groupings_from(@assignment.id)
          @target.reload
          @target.groupings.each do |grouping|
            grouping.accepted_students.each do |student|
              grouping.group.access_repo do |repo|
                assert_equal repo.get_permissions(student.user_name), Repository::Permission::READ_WRITE, "student should have read-write permissions on their group's repository"
              end
            end
          end
        end
      end
    end
  end

  context 'An assignment instance' do
    setup do
      @assignment = Assignment.make({:group_min => 1,
                                     :group_max => 1,
                                     :student_form_groups => false,
                                     :invalid_override => true,
                                     :due_date => 2.days.ago,
                                     :created_at => 42.days.ago })
    end

    context 'with a grouping that has a submission and a TA assigned ' do
      setup do
        @grouping = Grouping.make(:assignment => @assignment)
        @tamembership = TaMembership.make(:grouping => @grouping)
        @studentmembership = StudentMembership.make(:grouping => @grouping, :membership_status => StudentMembership::STATUSES[:inviter])
        @submission = Submission.make(:grouping => @grouping)
      end

      should 'be able to generate a simple CSV report of marks' do
        expected_string = ''
        Student.all.each do |student|
          fields = []
          fields.push(student.user_name)
          grouping = student.accepted_grouping_for(@assignment.id)
          if grouping.nil? || !grouping.has_submission?
            fields.push('')
          else
            submission = grouping.current_submission_used
            fields.push(submission.get_latest_result.total_mark / @assignment.total_mark * 100)
          end
          expected_string += fields.to_csv
        end
        assert_equal expected_string, @assignment.get_simple_csv_report, 'Simple CSV report is wrong!'
      end

      should 'be able to get a list of repository access URLs for each group' do
        expected_string = ''
        @assignment.groupings.each do |grouping|
          group = grouping.group
          expected_string += [group.group_name,group.repository_external_access_url].to_csv
        end
        assert_equal expected_string, @assignment.get_svn_repo_list, 'Repo access url list string is wrong!'
      end

      context 'with two groups of a single student each' do
        setup do
          (1..2).each do
            g = Grouping.make(:assignment => @assignment)
            # StudentMembership.make({:grouping => g,:membership_status => StudentMembership::STATUSES[:inviter] } )
            s = Submission.make(:grouping => g)
            r = s.get_latest_result
            (1..2).each do
              Mark.make(:result => r)
            end
            r.reload
            r.marking_state = Result::MARKING_STATES[:complete]
            r.save
          end
        end

        should 'be able to get_svn_export_commands' do
          expected_array = []

          @assignment.groupings.each do |grouping|
            submission = grouping.current_submission_used
            if submission
              group = grouping.group
              expected_array.push("svn export -r #{submission.revision_number} #{REPOSITORY_EXTERNAL_BASE_URL}/#{group.repository_name}/#{@assignment.repository_folder} \"#{group.group_name}\"")
            end
          end
          assert_equal expected_array, @assignment.get_svn_export_commands
        end

        should 'be able to get_svn_export_commands with spaces in group name ' do
          Group.all.each do |group|
            group.group_name = group.group_name + ' Test'
            group.save
          end
          expected_array = []

          @assignment.groupings.each do |grouping|
            submission = grouping.current_submission_used
            if submission
              group = grouping.group
              expected_array.push("svn export -r #{submission.revision_number} #{REPOSITORY_EXTERNAL_BASE_URL}/#{group.repository_name}/#{@assignment.repository_folder} \"#{group.group_name}\"")
            end
          end
          assert_equal expected_array, @assignment.get_svn_export_commands
        end
      end

      context 'with two groups of a single student each with multiple submission' do
        setup do
          (1..2).each do
            g = Grouping.make(:assignment => @assignment)
            # create 2 submission for each group
            (1..2).each do
              s = Submission.make(:grouping => g)
              r = s.get_latest_result
              (1..2).each do
                Mark.make(:result => r)
              end
              r.reload
              r.marking_state = Result::MARKING_STATES[:complete]
              r.save
            end
            g.save
          end
        end

        should 'be able to get_svn_export_commands' do
          expected_array = []

          @assignment.groupings.each do |grouping|
            submission = grouping.current_submission_used
            if submission
              group = grouping.group
              expected_array.push("svn export -r #{submission.revision_number} #{REPOSITORY_EXTERNAL_BASE_URL}/#{group.repository_name}/#{@assignment.repository_folder} \"#{group.group_name}\"")
            end
          end
          assert_equal expected_array, @assignment.get_svn_export_commands
        end
      end
    end
  end # end assignment instance context
end
