# frozen_string_literal: true

#
# Copyright (C) 2011 - 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/>.
#

require "feedjira"

describe GroupsController do
  before :once do
    course_with_teacher(active_all: true)
    students = create_users_in_course(@course, 3, return_type: :record)
    @student1, @student2, @student3 = students
    @student = @student1
  end

  describe "GET context_index" do
    context "student context cards" do
      it "is always enabled for teachers" do
        %w[manage_students allow_course_admin_actions].each do |perm|
          RoleOverride.manage_role_override(Account.default, teacher_role, perm, override: false)
        end
        user_session(@teacher)
        get "index", params: { course_id: @course.id }
        expect(assigns[:js_env][:STUDENT_CONTEXT_CARDS_ENABLED]).to be true
      end

      it "is always disabled for students" do
        user_session(@student)
        get "index", params: { course_id: @course.id }
        cards_enabled = assigns[:js_env] && assigns[:js_env][:STUDENT_CONTEXT_CARDS_ENABLED]
        expect(cards_enabled).to be_falsey
      end
    end

    it "requires authorization" do
      user_session(user_factory) # logged in user_factory without course access
      category1 = @course.group_categories.create(name: "category 1")
      category2 = @course.group_categories.create(name: "category 2")
      @course.groups.create(name: "some group", group_category: category1)
      @course.groups.create(name: "some other group", group_category: category1)
      @course.groups.create(name: "some third group", group_category: category2)
      get "index", params: { course_id: @course.id }
      assert_unauthorized
    end

    it "assigns variables" do
      user_session(@teacher)
      category1 = @course.group_categories.create(name: "category 1")
      category2 = @course.group_categories.create(name: "category 2")
      g1 = @course.groups.create(name: "some group", group_category: category1)
      g2 = @course.groups.create(name: "some other group", group_category: category1)
      g3 = @course.groups.create(name: "some third group", group_category: category2)
      get "index", params: { course_id: @course.id }
      expect(response).to be_successful
      expect(assigns[:groups]).not_to be_empty
      expect(assigns[:groups].length).to be(3)
      expect(assigns[:groups] - [g1, g2, g3]).to be_empty
      expect(assigns[:categories].length).to be(2)
    end

    it "js_env group_categories only includes non-deleted categories" do
      user_session(@teacher)
      active_category = @course.group_categories.create!(name: "Active Category")
      deleted_category = @course.group_categories.create!(name: "Deleted Category")
      deleted_category.update!(deleted_at: Time.now.utc)

      get "index", params: { course_id: @course.id }

      group_categories = assigns[:js_env][:group_categories]
      expect(group_categories.pluck("id")).to contain_exactly(active_category.id)
    end

    it "returns groups in sorted by group category name, then group name for student view" do
      user_session(@student)
      category1 = @course.group_categories.create(name: "1")
      category2 = @course.group_categories.create(name: "2")
      category3 = @course.group_categories.create(name: "11")
      groups = []
      groups << @course.groups.create(name: "11", group_category: category1)
      groups << @course.groups.create(name: "2", group_category: category1)
      groups << @course.groups.create(name: "1", group_category: category1)
      groups << @course.groups.create(name: "22", group_category: category2)
      groups << @course.groups.create(name: "2", group_category: category2)
      groups << @course.groups.create(name: "3", group_category: category2)
      groups << @course.groups.create(name: "4", group_category: category3)
      groups << @course.groups.create(name: "44", group_category: category3)
      groups << @course.groups.create(name: "4.5", group_category: category3)
      groups.each { |g| g.add_user @student, "accepted" }
      get "index", params: { course_id: @course.id, per_page: 50 }, format: "json"
      expect(response).to be_successful
      expect(assigns[:paginated_groups]).not_to be_empty
      expect(assigns[:paginated_groups].length).to be(9)
      # Check group category ordering
      expect(assigns[:paginated_groups][0].group_category.name).to eql("1")
      expect(assigns[:paginated_groups][1].group_category.name).to eql("1")
      expect(assigns[:paginated_groups][2].group_category.name).to eql("1")
      expect(assigns[:paginated_groups][3].group_category.name).to eql("2")
      expect(assigns[:paginated_groups][4].group_category.name).to eql("2")
      expect(assigns[:paginated_groups][5].group_category.name).to eql("2")
      expect(assigns[:paginated_groups][6].group_category.name).to eql("11")
      expect(assigns[:paginated_groups][7].group_category.name).to eql("11")
      expect(assigns[:paginated_groups][8].group_category.name).to eql("11")
      # Check group name ordering
      expect(assigns[:paginated_groups][0].name).to eql("1")
      expect(assigns[:paginated_groups][1].name).to eql("2")
      expect(assigns[:paginated_groups][2].name).to eql("11")
      expect(assigns[:paginated_groups][3].name).to eql("2")
      expect(assigns[:paginated_groups][4].name).to eql("3")
      expect(assigns[:paginated_groups][5].name).to eql("22")
      expect(assigns[:paginated_groups][6].name).to eql("4")
      expect(assigns[:paginated_groups][7].name).to eql("4.5")
      expect(assigns[:paginated_groups][8].name).to eql("44")
    end

    it "does not 500 for admins that can view but cannot manage groups" do
      a = Account.default
      role = custom_account_role("groups-view-only", account: a)
      a.role_overrides.create! role:, permission: "manage_groups", enabled: false
      a.role_overrides.create! role:, permission: "read_roster", enabled: true
      a.role_overrides.create! role:, permission: "view_group_pages", enabled: true

      my_admin = User.create!(name: "my admin")
      a.account_users.create!(user: my_admin, role:)

      course_with_teacher(active_all: true)
      user_session(my_admin)
      category1 = @course.group_categories.create(name: "category 1")
      @course.groups.create(name: "some group", group_category: category1)
      get "index", params: { course_id: @course.id, section_restricted: true }, format: :json
      expect(response).to have_http_status :ok
    end

    it "don't filter out inactive students if json and param set" do
      course_with_teacher(active_all: true)
      students = create_users_in_course(@course, 2, return_type: :record)
      student1, student2 = students
      category1 = @course.group_categories.create(name: "category 1")
      g = @course.groups.create(name: "some group", group_category: category1)
      g.add_user(student1)
      g.add_user(student2)
      student2.enrollments.first.deactivate
      user_session(student1)
      get "index",
          params: { course_id: @course.id,
                    include: "users",
                    include_inactive_users: true },
          format: :json
      parsed_json = json_parse(response.body)
      expect(parsed_json.length).to eq 1
      users_json = parsed_json.first["users"]
      expect(users_json).not_to be_nil
      expect(users_json.length).to eq 2
      ids_json = users_json.to_set { |u| u["id"] }
      expect(ids_json).to eq [student1.id, student2.id].to_set
      names_json = users_json.to_set { |u| u["name"] }
      expect(names_json).to eq [student1.name, student2.name].to_set
      expect(response).to be_successful
    end

    context "section_restricted" do
      before do
        # Create a section restricted user in their own section
        @section1 = @course.course_sections.first
        @other_section = @course.course_sections.create!(name: "Other Section")
        @section_restricted_student = @course.enroll_student(user_model, section: @other_section, enrollment_state: "active").user
        @section_restricted_student_2 = @course.enroll_student(user_model, section: @other_section, enrollment_state: "active").user
        @section_restricted_student_3 = @course.enroll_student(user_model, section: @other_section, enrollment_state: "active").user
        @other_student = @course.enroll_student(user_model, section: @section1, enrollment_state: "active").user
        Enrollment.limit_privileges_to_course_section!(@course, @section_restricted_student, true)
        Enrollment.limit_privileges_to_course_section!(@course, @section_restricted_student_2, true)
        Enrollment.limit_privileges_to_course_section!(@course, @section_restricted_student_3, true)
      end

      context "teacher assigned group category" do
        before do
          # Create a groupset that allows self-signup and requires group members to be in same section
          @group_category = GroupCategory.create(name: "Groups", context: @course)
          @group_category.save!
        end

        it "does not show section restricted students groups with non-section members" do
          # Group with the restricted user
          group_with_section_restricted_user = @course.groups.create(name: "restricted 1", group_category: @group_category)
          group_with_section_restricted_user.add_user(@section_restricted_student)
          group_with_section_restricted_user.save

          # Group with student from another section
          group_with_user_in_different_section = @course.groups.create(name: "different section", group_category: @group_category)
          group_with_user_in_different_section.add_user(@other_student)
          group_with_user_in_different_section.save

          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(1)
          expect(assigns[:groups].length).to be(1)
          expect(assigns[:groups][0].id).to eql(group_with_section_restricted_user.id)
        end

        it "does not hide group if you are a group member" do
          # Group with a section_restricted user
          group_with_section_restricted_user = @course.groups.create(name: "restricted 1", group_category: @group_category)
          group_with_section_restricted_user.add_user(@section_restricted_student_2)
          group_with_section_restricted_user.save

          # Group with student from another section
          group_with_user_in_different_section = @course.groups.create(name: "different section", group_category: @group_category)
          group_with_user_in_different_section.add_user(@other_student)
          group_with_user_in_different_section.add_user(@section_restricted_student)
          group_with_user_in_different_section.save

          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(2)
          expect(assigns[:groups].length).to be(2)
          expect(assigns[:groups].map(&:id).sort).to eql(@group_category.groups.map(&:id).sort)
        end
      end

      context "self-signup non-section restricted group category" do
        it "does not hide any groups from section restricted students" do
          # Group Category that does not restrict sections of students
          @group_category_non_restricted = GroupCategory.student_organized_for(@course)
          @group_category_non_restricted.configure_self_signup(true, false)
          @group_category_non_restricted.save!

          # Group with the restricted user
          group_with_section_restricted_user = @course.groups.create(name: "restricted 1", group_category: @group_category_non_restricted)
          group_with_section_restricted_user.add_user(@section_restricted_student)
          group_with_section_restricted_user.save

          # Group with student from another section
          group_with_user_in_different_section = @course.groups.create(name: "different section", group_category: @group_category_non_restricted)
          group_with_user_in_different_section.add_user(@other_student)
          group_with_user_in_different_section.save

          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(2)
          expect(assigns[:groups].length).to be(2)
          expect(assigns[:groups].map(&:id).sort).to eql(@group_category_non_restricted.groups.map(&:id).sort)
        end
      end

      context "self-signup restricted group category" do
        before do
          # Create a groupset that allows self-signup and requires group members to be in same section
          @group_category = GroupCategory.student_organized_for(@course)
          @group_category.configure_self_signup(true, true)
          @group_category.group_limit = 2
          @group_category.save!
        end

        it "does not remove groups for teachers" do
          # Group with the restricted user
          group_with_section_restricted_user = @course.groups.create(name: "restricted 1", group_category: @group_category)
          group_with_section_restricted_user.add_user(@section_restricted_student)
          group_with_section_restricted_user.save

          # Group with student from another section
          group_with_user_in_different_section = @course.groups.create(name: "different section", group_category: @group_category)
          group_with_user_in_different_section.add_user(@other_student)
          group_with_user_in_different_section.save

          # Teacher
          user_session(@teacher)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(2)
          expect(assigns[:groups].length).to be(2)
          expect(assigns[:groups].map(&:id).sort).to eql(@group_category.groups.map(&:id).sort)
        end

        it "does not remove empty groups" do
          # Empty Group
          group_with_user_in_different_section = @course.groups.create(name: "Empty group", group_category: @group_category)
          group_with_user_in_different_section.save

          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(1)
          expect(assigns[:groups].length).to be(1)
          expect(assigns[:groups][0].id).to eql(group_with_user_in_different_section.id)
        end

        it "does not remove full groups if users have the same section as the current user" do
          # Group with the second restricted user
          group_with_section_restricted_user_2 = @course.groups.create(name: "restricted and full group", group_category: @group_category)
          group_with_section_restricted_user_2.add_user(@section_restricted_student_2)
          group_with_section_restricted_user_2.add_user(@section_restricted_student_3)
          group_with_section_restricted_user_2.save

          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(1)
          expect(assigns[:groups].length).to be(1)
          expect(assigns[:groups][0].id).to eql(group_with_section_restricted_user_2.id)
        end

        it "does not show groups with students from other sections to section restricted students" do
          # Group with the restricted user
          group_with_section_restricted_user = @course.groups.create(name: "restricted 1", group_category: @group_category)
          group_with_section_restricted_user.add_user(@section_restricted_student)
          group_with_section_restricted_user.save

          # Group with student from another section
          group_with_user_in_different_section = @course.groups.create(name: "different section", group_category: @group_category)
          group_with_user_in_different_section.add_user(@other_student)
          group_with_user_in_different_section.save
          # Restricted user is logged in
          user_session(@section_restricted_student)
          get "index", params: { course_id: @course.id, section_restricted: true }, format: :json

          expect(response).to be_successful
          expect(json_parse(response.body).length).to be(1)
          expect(assigns[:groups].length).to be(1)
          expect(assigns[:groups][0].id).to eql(group_with_section_restricted_user.id)
        end
      end
    end

    context "self_signup_deadline_enabled ENV variable" do
      it "set to true if enabled at account level" do
        @course.account.enable_feature!(:self_signup_deadline)
        user_session(@teacher)
        get "index", params: { course_id: @course.id }
        expect(assigns[:js_env][:self_signup_deadline_enabled]).to be_truthy
      end

      it "set to false if not enabled at account level" do
        user_session(@teacher)
        get "index", params: { course_id: @course.id }
        expect(assigns[:js_env][:self_signup_deadline_enabled]).to be_falsey
      end
    end

    context "context_groups renders successfully" do
      it "for a student" do
        user_session(@student)
        get "index", params: { course_id: @course.id }, format: :html
        expect(response).to be_successful
      end

      it "for a teacher" do
        user_session(@teacher)
        get "index", params: { course_id: @course.id }, format: :html
        expect(response).to be_successful
      end

      context "with deprecate_context_groups_old_view FF enabled" do
        before do
          Account.site_admin.enable_feature!(:deprecate_context_groups_old_view)
        end

        it "for a student" do
          user_session(@student)
          get "index", params: { course_id: @course.id }, format: :html
          expect(response).to be_successful
        end

        it "for a teacher" do
          user_session(@teacher)
          get "index", params: { course_id: @course.id }, format: :html
          expect(response).to be_successful
        end
      end
    end

    it "forces IS_LARGE_ROSTER to true for account groups" do
      account_admin_user
      @account = Account.default
      user_session(@admin)
      get "index", params: { account_id: @account.id }, format: :html
      expect(assigns[:js_env][:IS_LARGE_ROSTER]).to be true
    end
  end

  describe "group_json" do
    it "should include context_name for group" do
      group_with_user(group_context: @course, user: @student, active_all: true)
      user_session(@student)

      get "index", format: "json"
      expect(response).to be_successful
      parsed_json = json_parse(response.body)
      expect(parsed_json.length).to eq 1
      expect(parsed_json[0]["context_name"]).to eq @group.context.name
    end

    it "should include course_id and not account_id if group's context is course'" do
      group_with_user(group_context: @course, user: @student, active_all: true)
      user_session(@student)
      get "index", format: "json"
      parsed_json = json_parse(response.body)
      expect(parsed_json.length).to eq 1
      expect(parsed_json[0]["course_id"]).to eq @group.context.id
      expect(parsed_json[0]["account_id"]).to be_nil
    end

    it "should include account_id and not course_id if group's context is account" do
      group_with_user(group_context: @account, user: @student, active_all: true)
      user_session(@student)
      get "index", format: "json"
      parsed_json = json_parse(response.body)
      expect(parsed_json.length).to eq 1
      expect(parsed_json[0]["account_id"]).to eq @group.context.id
      expect(parsed_json[0]["course_id"]).to be_nil
    end
  end

  describe "GET index" do
    it "splits up current and previous groups" do
      course1 = @course
      group_with_user(group_context: course1, user: @student, active_all: true)
      group1 = @group

      course_with_teacher(active_all: true)
      course2 = @course

      course2.soft_conclude!
      course2.save!

      create_enrollments(course2, [@student])
      group_with_user(group_context: course2, user: @student, active_all: true)
      group2 = @group

      user_session(@student)

      get "index"
      expect(assigns[:current_groups]).to eq([group1])
      expect(assigns[:previous_groups]).to eq([group2])
    end

    it "does not show restricted previous groups" do
      group_with_user(group_context: @course, user: @student, active_all: true)

      @course.soft_conclude!
      @course.restrict_student_past_view = true
      @course.save!

      user_session(@student)

      get "index"
      expect(assigns[:current_groups]).to eq([])
      expect(assigns[:previous_groups]).to eq([])
    end

    it 'puts groups in courses in terms concluded for students in "previous groups"' do
      @course.enrollment_term.set_overrides(@course.account, "StudentEnrollment" => { end_at: 1.week.ago })
      group_with_user(group_context: @course, user: @student, active_all: true)
      user_session(@student)
      get "index"
      expect(assigns[:current_groups]).to eq([])
      expect(assigns[:previous_groups]).to eq([@group])
    end

    describe "pagination" do
      before :once do
        group_with_user(group_context: @course, user: @student, active_all: true)
        group_with_user(group_context: @course, user: @student, active_all: true)
      end

      before do
        user_session(@student)
      end

      it "does not paginate non-json" do
        get "index", params: { per_page: 1 }
        expect(assigns[:current_groups]).to eq @student.current_groups.by_name
        expect(response.headers["Link"]).to be_nil
      end

      it "paginates json" do
        get "index", params: { per_page: 1 }, format: "json"
        expect(assigns[:groups]).to eq [@student.current_groups.by_name.first]
        expect(response.headers["Link"]).not_to be_nil
      end
    end
  end

  describe "GET show" do
    it "requires authorization" do
      @group = Account.default.groups.create!(name: "some group")
      get "show", params: { id: @group.id }
      expect(assigns[:group]).to eql(@group)
      assert_unauthorized
    end

    it "assigns variables" do
      @group = Account.default.groups.create!(name: "some group")
      @user = user_model
      user_session(@user)
      @group.add_user(@user)
      get "show", params: { id: @group.id }
      expect(response).to be_successful
      expect(assigns[:group]).to eql(@group)
      expect(assigns[:context]).to eql(@group)
      expect(assigns[:stream_items]).to eql([])
    end

    it "allows user to join self-signup groups" do
      user_session(@student)
      category1 = @course.group_categories.create!(name: "category 1")
      category1.configure_self_signup(true, false)
      category1.save!
      g1 = @course.groups.create!(name: "some group", group_category: category1)

      get "show", params: { course_id: @course.id, id: g1.id, join: 1 }
      g1.reload
      expect(g1.users.map(&:id)).to include @student.id
    end

    it "allows user to leave self-signup groups" do
      user_session(@student)
      category1 = @course.group_categories.create!(name: "category 1")
      category1.configure_self_signup(true, false)
      category1.save!
      g1 = @course.groups.create!(name: "some group", group_category: category1)
      g1.add_user(@student)

      get "show", params: { course_id: @course.id, id: g1.id, leave: 1 }
      g1.reload
      expect(g1.users.map(&:id)).not_to include @student.id
    end

    it "allows user to join student organized groups" do
      user_session(@student)
      category1 = GroupCategory.student_organized_for(@course)
      g1 = @course.groups.create!(name: "some group", group_category: category1, join_level: "parent_context_auto_join")

      get "show", params: { course_id: @course.id, id: g1.id, join: 1 }
      g1.reload
      expect(g1.users.map(&:id)).to include @student.id
    end

    it "allows user to leave student organized groups" do
      user_session(@student)
      category1 = @course.group_categories.create!(name: "category 1", role: "student_organized")
      g1 = @course.groups.create!(name: "some group", group_category: category1)
      g1.add_user(@student)

      get "show", params: { course_id: @course.id, id: g1.id, leave: 1 }
      g1.reload
      expect(g1.users.map(&:id)).not_to include @student.id
    end

    it "allows teachers to view after conclusion" do
      @teacher.enrollments.first.conclude
      user_session(@teacher)
      category = @course.group_categories.create(name: "category")
      group = @course.groups.create(name: "some group", group_category: category)

      get "show", params: { id: group.id }

      expect(response).to be_successful
      expect(assigns[:group]).to eql(group)
    end
  end

  describe "GET new" do
    it "requires authorization" do
      @group = @course.groups.create!(name: "some group")
      get "new", params: { course_id: @course.id }
      assert_unauthorized
    end
  end

  describe "POST add_user" do
    it "requires authorization" do
      @group = Account.default.groups.create!(name: "some group")
      post "add_user", params: { group_id: @group.id }
      assert_unauthorized
    end

    it "adds user" do
      user_session(@teacher)
      @group = @course.groups.create!(name: "PG 1", group_category: @category)
      @user = user_factory(active_all: true)
      post "add_user", params: { group_id: @group.id, user_id: @user.id }
      expect(response).to be_successful
      expect(assigns[:membership]).not_to be_nil
      expect(assigns[:membership].user).to eql(@user)
    end

    it "checks user section in restricted self-signup category" do
      user_session(@teacher)
      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_category = @course.group_categories.build(name: "My Category")
      group_category.configure_self_signup(true, true)
      group_category.save
      group = group_category.groups.create(context: @course)
      group.add_user(user1)

      post "add_user", params: { group_id: group.id, user_id: user2.id }
      expect(response).not_to be_successful
      expect(assigns[:membership]).not_to be_nil
      expect(assigns[:membership].user).to eql(user2)
      expect(assigns[:membership].errors[:user_id]).not_to be_nil
    end
  end

  describe "DELETE remove_user" do
    it "requires authorization" do
      @group = Account.default.groups.create!(name: "some group")
      @user = user_factory(active_all: true)
      @group.add_user(@user)
      delete "remove_user", params: { group_id: @group.id, user_id: @user.id, id: @user.id }
      assert_unauthorized
    end

    it "removes user" do
      user_session(@teacher)
      @group = @course.groups.create!(name: "PG 1", group_category: @category)
      @group.add_user(@user)
      delete "remove_user", params: { group_id: @group.id, user_id: @user.id, id: @user.id }
      expect(response).to be_successful
      @group.reload
      expect(@group.users).to be_empty
    end
  end

  describe "POST create" do
    it "requires authorization" do
      post "create", params: { course_id: @course.id, group: { name: "some group" } }
      assert_unauthorized
    end

    it "creates new group" do
      user_session(@teacher)
      post "create", params: { course_id: @course.id, group: { name: "some group" } }
      expect(response).to be_redirect
      expect(assigns[:group]).not_to be_nil
      expect(assigns[:group].name).to eql("some group")
    end

    it "does not create new group if :manage_groups_add is not enabled" do
      @course.account.role_overrides.create!(
        permission: "manage_groups_add",
        role: teacher_role,
        enabled: false
      )
      user_session(@teacher)
      post "create", params: { course_id: @course.id, group: { name: "some group" } }
      assert_unauthorized
    end

    it "honors group[group_category_id] when permitted" do
      user_session(@teacher)
      group_category = @course.group_categories.create(name: "some category")
      post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: group_category.id } }
      expect(response).to be_redirect
      expect(assigns[:group]).not_to be_nil
      expect(assigns[:group].group_category).to eq group_category
    end

    it "does not honor group[group_category_id] when not permitted" do
      user_session(@student)
      group_category = @course.group_categories.create(name: "some category")
      post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: group_category.id } }
      expect(response).to be_redirect
      expect(assigns[:group]).not_to be_nil
      expect(assigns[:group].group_category).to eq GroupCategory.student_organized_for(@course)
    end

    it "fails when group[group_category_id] would be honored but doesn't exist" do
      user_session(@student)
      @course.group_categories.create(name: "some category")
      post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: 11_235 } }
      expect(response).not_to be_successful
    end

    describe "quota" do
      before do
        Setting.set("group_default_quota", 11.decimal_megabytes)
      end

      context "teacher" do
        before do
          user_session(@teacher)
        end

        it "ignores the storage_quota_mb parameter" do
          post "create", params: { course_id: @course.id, group: { name: "a group", storage_quota_mb: 22 } }
          expect(assigns[:group].storage_quota_mb).to eq 11
        end
      end

      context "account admin" do
        before do
          account_admin_user
          user_session(@admin)
        end

        it "sets the storage_quota_mb parameter" do
          post "create", params: { course_id: @course.id, group: { name: "a group", storage_quota_mb: 22 } }
          expect(assigns[:group].storage_quota_mb).to eq 22
        end
      end
    end
  end

  describe "PUT update" do
    it "requires authorization" do
      @group = @course.groups.create!(name: "some group")
      put "update", params: { course_id: @course.id, id: @group.id, group: { name: "new name" } }
      assert_unauthorized
    end

    it "updates group" do
      user_session(@teacher)
      @group = @course.groups.create!(name: "some group")
      put "update", params: { course_id: @course.id, id: @group.id, group: { name: "new name" } }
      expect(response).to be_redirect
      expect(assigns[:group]).to eql(@group)
      expect(assigns[:group].name).to eql("new name")
    end

    it "does not update group if :manage_groups_manage is not enabled" do
      @course.account.role_overrides.create!(
        permission: "manage_groups_manage",
        role: teacher_role,
        enabled: false
      )
      user_session(@teacher)
      @group = @course.groups.create!(name: "some group")
      put "update", params: { course_id: @course.id, id: @group.id, group: { name: "new name" } }
      assert_unauthorized
    end

    it "honors group[group_category_id]" do
      user_session(@teacher)
      group_category = @course.group_categories.create(name: "some category")
      @group = @course.groups.create!(name: "some group")
      put "update", params: { course_id: @course.id, id: @group.id, group: { group_category_id: group_category.id } }
      expect(response).to be_redirect
      expect(assigns[:group]).to eql(@group)
      expect(assigns[:group].group_category).to eq group_category
    end

    it "fails when group[group_category_id] doesn't exist" do
      user_session(@teacher)
      group_category = @course.group_categories.create(name: "some category")
      @group = @course.groups.create!(name: "some group", group_category:)
      put "update", params: { course_id: @course.id, id: @group.id, group: { group_category_id: 11_235 } }
      expect(response).not_to be_successful
    end

    it "is able to unset a leader" do
      user_session(@teacher)
      @group = @course.groups.create!(name: "some group")
      @group.add_user(@student1)
      @group.update_attribute(:leader, @student1)
      put "update", params: { course_id: @course.id, id: @group.id, group: { leader: nil } }
      expect(@group.reload.leader).to be_nil
    end

    it "doesn't overwrite stuck sis fields" do
      user_session(@teacher)
      original_name = "some group"
      @group = @course.groups.create!(name: original_name)
      put "update", params: { course_id: @course.id, id: @group.id, override_sis_stickiness: false, group: { name: "new name" } }

      expect(response).to be_redirect
      expect(assigns[:group]).to eql(@group)
      expect(assigns[:group].name).to eql(original_name)
    end

    describe "quota" do
      before :once do
        @group = @course.groups.build(name: "teh gruop")
        @group.storage_quota_mb = 11
        @group.save!
      end

      context "teacher" do
        before do
          user_session(@teacher)
        end

        it "ignores the quota parameter" do
          put "update", params: { course_id: @course.id, id: @group.id, group: { name: "the group", storage_quota_mb: 22 } }
          @group.reload
          expect(@group.name).to eq "the group"
          expect(@group.storage_quota_mb).to eq 11
        end
      end

      context "account admin" do
        before do
          account_admin_user
          user_session(@admin)
        end

        it "updates group quota" do
          put "update", params: { course_id: @course.id, id: @group.id, group: { name: "the group", storage_quota_mb: 22 } }
          @group.reload
          expect(@group.name).to eq "the group"
          expect(@group.storage_quota_mb).to eq 22
        end
      end
    end
  end

  describe "DELETE destroy" do
    it "requires authorization" do
      @group = @course.groups.create!(name: "some group")
      delete "destroy", params: { course_id: @course.id, id: @group.id }
      assert_unauthorized
    end

    it "deletes group" do
      user_session(@teacher)
      @group = @course.groups.create!(name: "some group")
      delete "destroy", params: { course_id: @course.id, id: @group.id }
      expect(assigns[:group]).to eql(@group)
      expect(assigns[:group]).not_to be_frozen
      expect(assigns[:group]).to be_deleted
      expect(@course.groups).to include(@group)
      expect(@course.groups.active).not_to include(@group)
    end

    it "does not delete group if :manage_groups_delete is not enabled" do
      @course.account.role_overrides.create!(
        permission: "manage_groups_delete",
        role: teacher_role,
        enabled: false
      )
      user_session(@teacher)
      @group = @course.groups.create!(name: "some group")
      delete "destroy", params: { course_id: @course.id, id: @group.id }
      assert_unauthorized
    end
  end

  describe "GET 'unassigned_members'" do
    it "includes all users if the category is student organized" do
      user_session(@teacher)
      u1 = @student1
      u2 = @student2
      u3 = @student3

      group = @course.groups.create(name: "Group 1", group_category: GroupCategory.student_organized_for(@course))
      group.add_user(u1)
      group.add_user(u2)

      get "unassigned_members", params: { course_id: @course.id, category_id: group.group_category.id }
      expect(response).to be_successful
      data = json_parse
      expect(data).not_to be_nil
      expect(data["users"].pluck("user_id").sort)
        .to eq [u1, u2, u3].map(&:id).sort
    end

    it "includes only users not in a group in the category otherwise" do
      user_session(@teacher)
      u1 = @student1
      u2 = @student2
      u3 = @student3

      group_category1 = @course.group_categories.create(name: "Group Category 1")
      group1 = @course.groups.create(name: "Group 1", group_category: group_category1)
      group1.add_user(u1)

      group_category2 = @course.group_categories.create(name: "Group Category 2")
      group2 = @course.groups.create(name: "Group 1", group_category: group_category2)
      group2.add_user(u2)

      group_category3 = @course.group_categories.create(name: "Group Category 3")
      group3 = @course.groups.create(name: "Group 1", group_category: group_category3)
      group3.add_user(u2)
      group3.add_user(u3)

      get "unassigned_members", params: { course_id: @course.id, category_id: group1.group_category.id }
      expect(response).to be_successful
      data = json_parse
      expect(data).not_to be_nil
      expect(data["users"].pluck("user_id").sort)
        .to eq [u2, u3].map(&:id).sort

      get "unassigned_members", params: { course_id: @course.id, category_id: group2.group_category.id }
      expect(response).to be_successful
      data = json_parse
      expect(data).not_to be_nil
      expect(data["users"].pluck("user_id").sort)
        .to eq [u1, u3].map(&:id).sort

      get "unassigned_members", params: { course_id: @course.id, category_id: group3.group_category.id }
      expect(response).to be_successful
      data = json_parse
      expect(data).not_to be_nil
      expect(data["users"].pluck("user_id")).to eq [u1.id]
    end

    it "includes the users' sections when available" do
      user_session(@teacher)
      u1 = @student1

      group = @course.groups.create(name: "Group 1", group_category: GroupCategory.student_organized_for(@course))
      group.add_user(u1)

      get "unassigned_members", params: { course_id: @course.id, category_id: group.group_category.id }
      data = json_parse
      expect(data["users"].first["sections"].first["section_id"]).to eq @course.default_section.id
      expect(data["users"].first["sections"].first["section_code"]).to eq @course.default_section.section_code
    end
  end

  describe "GET 'context_group_members'" do
    it "includes the users' sections when available" do
      user_session(@teacher)
      u1 = @student1
      group = @course.groups.create(name: "Group 1", group_category: GroupCategory.student_organized_for(@course))
      group.add_user(u1)

      get "context_group_members", params: { group_id: group.id }
      data = json_parse
      expect(data.first["sections"].first["section_id"]).to eq @course.default_section.id
      expect(data.first["sections"].first["section_code"]).to eq @course.default_section.section_code
    end

    it "requires :read_roster permission" do
      u1 = @student1
      u2 = @student2
      group = @course.groups.create(name: "Group 1")
      group.add_user(u1)

      # u1 in the group has :read_roster permission
      user_session(u1)
      get "context_group_members", params: { group_id: group.id }
      expect(response).to be_successful

      # u2 outside the group doesn't have :read_roster permission, since the
      # group isn't self-signup and is invitation only (clear controller
      # context permission cache, though)
      controller.instance_variable_set(:@context_all_permissions, nil)
      user_session(u2)
      get "context_group_members", params: { group_id: group.id }
      expect(response).not_to be_successful
    end
  end

  describe "GET 'public_feed.atom'" do
    before :once do
      group_with_user(active_all: true)
      @dt = @group.discussion_topics.create!(title: "hi", message: "intros", user: @user)
      @wp = @group.wiki_pages.create! title: "a page"
    end

    it "requires authorization" do
      get "public_feed", params: { feed_code: @group.feed_code + "x" }, format: "atom"
      expect(assigns[:problem]).to match(/The verification code is invalid/)
    end

    it "includes absolute path for rel='self' link" do
      get "public_feed", params: { feed_code: @group.feed_code }, format: "atom"
      feed = Feedjira.parse(response.body)
      expect(feed).not_to be_nil
      expect(feed.feed_url).to match(%r{http://})
    end

    it "includes an author for each entry" do
      get "public_feed", params: { feed_code: @group.feed_code }, format: "atom"
      feed = Feedjira.parse(response.body)
      expect(feed).not_to be_nil
      expect(feed.entries).not_to be_empty
      expect(feed.entries.all? { |e| e.author.present? }).to be_truthy
    end

    it "excludes unpublished things" do
      get "public_feed", params: { feed_code: @group.feed_code }, format: "atom"
      feed = Feedjira.parse(response.body)
      expect(feed.entries.size).to eq 2

      @wp.unpublish
      @dt.unpublish! # yes, you really have to shout to unpublish a discussion topic :(

      get "public_feed", params: { feed_code: @group.feed_code }, format: "atom"
      feed = Feedjira.parse(response.body)
      expect(feed.entries.size).to eq 0
    end
  end

  describe "GET 'accept_invitation'" do
    before :once do
      @communities = GroupCategory.communities_for(Account.default)
      group_model(group_category: @communities)
      user_factory(active_user: true)
      @membership = @group.add_user(@user, "invited", false)
    end

    before do
      user_session(@user)
    end

    it "creates invitations" do
      get "accept_invitation", params: { group_id: @group.id, uuid: @membership.uuid }
      @group.reload
      expect(@group.has_member?(@user)).to be_truthy
      expect(@group.group_memberships.where(workflow_state: "invited").count).to eq 0
    end

    it "rejects an invalid invitation uuid" do
      get "accept_invitation", params: { group_id: @group.id, uuid: @membership.uuid + "x" }
      @group.reload
      expect(@group.has_member?(@user)).to be_falsey
      expect(@group.group_memberships.where(workflow_state: "invited").count).to eq 1
    end
  end

  describe "GET users" do
    before do
      category = @course.group_categories.create(name: "Study Groups")
      @group = @course.groups.create(name: "some group", group_category: category)
      @group.add_user(@student)

      assignment = @course.assignments.create({
                                                name: "test assignment",
                                                group_category: category
                                              })
      file = Attachment.create! context: @student, filename: "homework.pdf", uploaded_data: StringIO.new("blah blah blah")
      @sub = assignment.submit_homework(@student, attachments: [file], submission_type: "online_upload")
    end

    it "includes group submissions if param is present" do
      user_session(@teacher)
      get "users", params: { group_id: @group.id, include: ["group_submissions"] }
      json = json_parse(response.body)

      expect(response).to be_successful
      expect(json.count).to equal 1
      expect(json[0]["group_submissions"][0]).to equal @sub.id
    end

    it "does not include group submissions if param is absent" do
      user_session(@teacher)
      get "users", params: { group_id: @group.id }
      json = json_parse(response.body)

      expect(response).to be_successful
      expect(json.count).to equal 1
      expect(json[0]["group_submissions"]).to equal nil
    end

    describe "inactive students" do
      before :once do
        course_with_teacher(active_all: true)
        students = create_users_in_course(@course, 3, return_type: :record)
        @student1, @student2, @student3 = students
        category1 = @course.group_categories.create(name: "category 1")
        @group = @course.groups.create(name: "some group", group_category: category1)
        @group.add_user(@student1)
        @group.add_user(@student2)
        @group.add_user(@student3)
        @student2.enrollments.first.deactivate
        @student3.enrollments.first.update(start_at: 1.day.from_now, end_at: 2.days.from_now) # technically "inactive" but not really
      end

      it "include active status if requested" do
        user_session(@teacher)
        get "users", params: { group_id: @group.id, include: ["active_status"] }
        json = json_parse(response.body)
        expect(json.length).to eq 3
        expect(json.detect { |r| r["id"] == @student1.id }["is_inactive"]).to be_falsey
        expect(json.detect { |r| r["id"] == @student2.id }["is_inactive"]).to be_truthy
        expect(json.detect { |r| r["id"] == @student3.id }["is_inactive"]).to be_falsey
      end

      it "don't include active status if not requested" do
        user_session(@teacher)
        get "users", params: { group_id: @group.id }
        json = json_parse(response.body)
        expect(json.first["is_inactive"]).to be_nil
      end
    end
  end

  describe "POST create_file" do
    let(:course) { Course.create! }
    let(:group_category) { course.group_categories.create!(name: "just a category") }
    let(:group) { course.groups.create!(name: "just a group", group_category:) }
    let(:assignment) { course.assignments.create!(title: "hi", submission_types: "online_upload") }

    context "as a teacher" do
      before do
        user_session(teacher)
      end

      let(:teacher) { course.enroll_teacher(User.create!, active_all: true).user }
      let(:progress) { Progress.last }
      let(:request_params) do
        { course_id: course.id, group_id: group.id, filename: "An attachment!", url: "http://nowhere" }
      end

      it "creates a Progress object with an assignment as the context when the assignment_id parameter is included" do
        put "create_file", params: request_params.merge({ assignment_id: assignment.id })
        expect(progress.context).to eq(assignment)
      end

      it "creates a Progress object with the current user as the context when no assignment parameter is included" do
        put "create_file", params: request_params
        expect(progress.context).to eq(teacher)
      end
    end

    context "as a student" do
      before do
        @student = course.enroll_student(User.create!, enrollment_state: "active").user
        group.add_user(@student, "accepted")
        user_session(@student)
      end

      let(:request_params) do
        { group_id: group.id, assignment_id: assignment.id, filename: "An attachment!", url: "http://nowhere" }
      end

      let(:created_attachment) { group.attachments.first }

      it "uses the 'submissions' folder for assignment submissions" do
        put "create_file", params: request_params.merge(submit_assignment: true)
        expect(created_attachment.folder).to eq group.submissions_folder
      end

      it "uses the default folder for non-submissions" do
        put "create_file", params: request_params
        expect(created_attachment.folder).to eq Folder.unfiled_folder(group)
      end

      it "does not check quota if submit_assignment is true" do
        put "create_file", params: request_params.merge(submit_assignment: true)
        expect_any_instance_of(Attachment).not_to receive(:get_quota)
      end

      context "in a limited access account" do
        before do
          course.root_account.enable_feature!(:allow_limited_access_for_students)
          course.account.settings[:enable_limited_access_for_students] = true
          course.account.save!
        end

        it "renders unauthorized" do
          put "create_file", params: request_params.merge(submit_assignment: true)
          expect(response.code.to_i).to be 401
        end
      end
    end
  end

  context "Differentiation Tags" do
    before do
      @course.account.settings[:allow_assign_to_differentiation_tags] = { value: true }
      @course.account.save!
      @course.account.reload
      course_with_teacher(active_all: true, user: @user)
      @non_collaborative_group_category = @course.group_categories.create!(name: "Test non collaborative", non_collaborative: true)
      @non_collaborative_group = @course.groups.create!(name: "Non Collaborative group", group_category: @non_collaborative_group_category)
      @collaborative_group_category = @course.group_categories.create!(name: "Test collaborative", non_collaborative: false)
      @collaborative_group = @course.groups.create!(name: "Collaborative group", group_category: @collaborative_group_category)
      user_session(@teacher)
    end

    describe "GET index" do
      it "does not return non collaborative groups" do
        @non_collaborative_group.add_user(@student)
        @collaborative_group.add_user(@student)
        user_session(@student)

        get "index", format: "json"
        parsed_json = json_parse(response.body)
        expect(parsed_json.length).to eq 1
        expect(parsed_json[0]["name"]).to eq "Collaborative group"
        expect(parsed_json[0]["id"]).to eq @collaborative_group.id
      end
    end

    describe "GET context_index" do
      it "filters out non collaborative groups when the user doesn't have permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "index", params: { course_id: @course.id, collaboration_state: "all" }
        expect(response).to be_successful
        expect(assigns[:groups]).not_to be_empty
        expect(assigns[:groups].length).to be(1)
        expect(assigns[:groups].first).to eq(@collaborative_group)
      end

      it "filters out non-collaborative groups by default" do
        get "index", params: { course_id: @course.id }
        expect(response).to be_successful
        expect(assigns[:groups]).not_to be_empty
        expect(assigns[:groups].length).to be(1)
        expect(assigns[:groups].first).to eq(@collaborative_group)
      end

      it "filters out collaborative groups" do
        get "index", params: { course_id: @course.id, collaboration_state: "non_collaborative" }
        expect(response).to be_successful
        expect(assigns[:groups]).not_to be_empty
        expect(assigns[:groups].length).to be(1)
        expect(assigns[:groups].first).to eq(@non_collaborative_group)
      end

      it "errors when a user without the correct permissions tries to get only non_collaborative groups" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "index", params: { course_id: @course.id, collaboration_state: "non_collaborative" }
        expect(response).to have_http_status(:unauthorized)
      end

      it "returns both collaborative and non-collaborative groups when requested" do
        get "index", params: { course_id: @course.id, collaboration_state: "all" }
        expect(response).to be_successful
        expect(assigns[:groups]).not_to be_empty
        group_ids = assigns[:groups].map(&:id)
        expect(group_ids).to include(@collaborative_group.id, @non_collaborative_group.id)
      end

      it "filters out non-collaborative groups when requested explicitly" do
        get "index", params: { course_id: @course.id, collaboration_state: "collaborative" }
        expect(response).to be_successful
        expect(assigns[:groups]).not_to be_empty
        expect(assigns[:groups].length).to be >= 1
        assigns[:groups].each do |group|
          expect(group.non_collaborative).to be false
        end
      end

      it "returns group category name when filtered by user id and non collaborative groups" do
        students = create_users_in_course(@course, 2, return_type: :record)
        student1, student2 = students
        @non_collaborative_group.add_user(student1)
        @non_collaborative_group.add_user(student2)
        user_session(@teacher)
        get "index",
            params: { course_id: @course.id,
                      collaboration_state: "non_collaborative",
                      user_id: student1.id },
            format: :json
        expect(response).to be_successful
        parsed_json = json_parse(response.body)
        expect(parsed_json.length).to eq 1
        expect(parsed_json.first.keys).to include "group_category_name"
        expect(parsed_json.first["group_category_name"]).to include @non_collaborative_group_category.name
      end
    end

    describe "GET show" do
      it "denies access for users when group is non-collaborative" do
        get "show", params: { id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
      end

      it "denies access for users without any manage tags permissions when group is non-collaborative and request is json format" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "show", params: { id: @non_collaborative_group.id }, format: :json
        expect(response).to have_http_status(:forbidden)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative and request is json format" do
        get "show", params: { id: @non_collaborative_group.id }, format: :json
        expect(response).to be_successful
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "show", params: { id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "PUT update" do
      it "denies access for users without any manage tags permissions when group is non-collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        put "update", params: { id: @non_collaborative_group.id, group: { name: "new name" } }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative" do
        put "update", params: { id: @non_collaborative_group.id, group: { name: "new name" } }
        expect(response).to be_redirect
        expect(@non_collaborative_group.reload.name).to eq "new name"
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        put "update", params: { id: @collaborative_group.id, group: { name: "new name" } }
        expect(response).to be_redirect
        expect(@collaborative_group.reload.name).to eq "new name"
      end
    end

    describe "POST create" do
      it "denies access for users without manage_tags_add permission" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: @non_collaborative_group_category.id } }

        expect(response).to have_http_status(:unauthorized)
        expect(Group.count).to eq(2)
      end

      it "does not deny access for users without manage_tags_add permission from creating normal group" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        expect(Group.count).to eq(2)

        post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: @collaborative_group_category.id } }
        expect(response).to be_redirect
        expect(Group.count).to eq(3)
      end

      it "works correctly for users with manage_tags_add permission" do
        expect(Group.count).to eq(2)
        post "create", params: { course_id: @course.id, group: { name: "some group", group_category_id: @non_collaborative_group_category.id } }

        expect(response).to be_redirect
        expect(Group.count).to eq(3)
      end
    end

    describe "DELETE destroy" do
      it "denies access for users without manage_tags_delete permission" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        delete "destroy", params: { course_id: @course.id, id: @non_collaborative_group.id }

        expect(response).to have_http_status(:unauthorized)
        expect(Group.exists?(@non_collaborative_group.id)).to be(true) # Group is not deleted
      end

      it "does noy deny access for users without manage_tags_delete permission from deleting normal group" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        delete "destroy", params: { course_id: @course.id, id: @collaborative_group.id }

        expect(@course.groups).to include(@collaborative_group)
        expect(@course.groups.active).not_to include(@collaborative_group)
      end

      it "works correctly for users with manage_tags_delete permission" do
        delete "destroy", params: { course_id: @course.id, id: @non_collaborative_group.id }

        expect(@course.differentiation_tags).to include(@non_collaborative_group)
        expect(@course.differentiation_tags.active).not_to include(@non_collaborative_group)
      end
    end

    describe "GET 'context_group_members'" do
      it "denies access for users without any manage tags permissions when group is non-collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "context_group_members", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative" do
        get "context_group_members", params: { group_id: @non_collaborative_group.id }
        expect(response).to be_successful
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "context_group_members", params: { group_id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "POST add_user" do
      it "denies access for users without any manage tags permissions when group is non-collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        post "add_user", params: { group_id: @non_collaborative_group.id, user_id: @student1.id }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative" do
        post "add_user", params: { group_id: @non_collaborative_group.id, user_id: @student1.id }
        expect(response).to be_successful
        expect(@non_collaborative_group.users).to include(@student1)
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        post "add_user", params: { group_id: @collaborative_group.id, user_id: @student1.id }
        expect(response).to be_successful
        expect(@collaborative_group.users).to include(@student1)
      end
    end

    describe "DELETE remove_user" do
      it "denies access for users without any manage tags permissions when group is non-collaborative" do
        @non_collaborative_group.add_user(@student1)
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        delete "remove_user", params: { group_id: @non_collaborative_group.id, user_id: @student1.id }
        expect(response).to have_http_status(:unauthorized)
        expect(@non_collaborative_group.users).to include(@student1)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative" do
        @non_collaborative_group.add_user(@student1)
        delete "remove_user", params: { group_id: @non_collaborative_group.id, user_id: @student1.id }
        expect(response).to be_successful
        expect(@non_collaborative_group.users).not_to include(@student1)
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        @collaborative_group.add_user(@student1)
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        delete "remove_user", params: { group_id: @collaborative_group.id, user_id: @student1.id }
        expect(response).to be_successful
        expect(@collaborative_group.users).not_to include(@student1)
      end
    end

    describe "POST invite" do
      it "denies access for users without any manage tags permissions when group is non-collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        post "invite", params: { group_id: @non_collaborative_group.id, invitees: ["student@example.com"] }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access for users with any manage tags permission when group is non-collaborative" do
        post "invite", params: { group_id: @non_collaborative_group.id, invitees: ["student@example.com"] }
        expect(response).to be_successful
      end

      it "allows access for users without manage tags permissions when group is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        post "invite", params: { group_id: @collaborative_group.id, invitees: ["student@example.com"] }
        expect(response).to be_successful
      end
    end

    describe "GET 'unassigned_members'" do
      it "denies access for users without any manage tags permissions when group category is non-collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "unassigned_members", params: { course_id: @course.id, category_id: @non_collaborative_group_category.id }
        expect(response).to have_http_status(:unauthorized)
        expect(json_parse["message"]).to eq "Not authorized to manage differentiation tag."
      end

      it "allows access for users with any manage tags permission when group category is non-collaborative" do
        get "unassigned_members", params: { course_id: @course.id, category_id: @non_collaborative_group_category.id }
        expect(response).to be_successful
      end

      it "allows access for users without manage tags permissions when group category is collaborative" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end

        get "unassigned_members", params: { course_id: @course.id, category_id: @collaborative_group_category.id }
        expect(response).to be_successful
      end
    end

    describe "GET 'public_feed'" do
      it "returns unauthorized when accessing non_collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "public_feed", params: { feed_code: @non_collaborative_group.feed_code }, format: "atom"
        expect(response).to have_http_status(:unauthorized)
        json = response.parsed_body
        expect(json["message"]).to eq "Not authorized to manage differentiation tag."
      end

      it "allows access to non_collaborative group with proper permissions" do
        get "public_feed", params: { feed_code: @non_collaborative_group.feed_code }, format: "atom"
        expect(response).to be_successful
      end

      it "allows access to collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "public_feed", params: { feed_code: @collaborative_group.feed_code }, format: "atom"
        expect(response).to be_successful
      end
    end

    describe "GET 'activity_stream'" do
      it "returns unauthorized when accessing non_collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "activity_stream", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
        json = response.parsed_body
        expect(json["message"]).to eq "Not authorized to manage differentiation tag."
      end

      it "allows access to non_collaborative group with proper permissions" do
        get "activity_stream", params: { group_id: @non_collaborative_group.id }
        expect(response).to be_successful
      end

      it "allows access to collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "activity_stream", params: { group_id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "GET 'activity_stream_summary'" do
      it "returns unauthorized when accessing non_collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "activity_stream_summary", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
        json = response.parsed_body
        expect(json["message"]).to eq "Not authorized to manage differentiation tag."
      end

      it "allows access to non_collaborative group with proper permissions" do
        get "activity_stream_summary", params: { group_id: @non_collaborative_group.id }
        expect(response).to be_successful
      end

      it "allows access to collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "activity_stream_summary", params: { group_id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "POST 'create_file'" do
      it "denies access to upload files when group is non-collaborative" do
        post "create_file", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
        json = response.parsed_body
        expect(json["message"]).to eq "Not authorized to upload file to Differentiation Tag"
      end
    end

    describe "GET 'users'" do
      it "denies access when group is non-collaborative and user lacks permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "users", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access when group is non-collaborative and user has permissions" do
        get "users", params: { group_id: @non_collaborative_group.id }
        expect(response).to be_successful
      end

      it "allows access when group is collaborative regardless of permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "users", params: { group_id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "GET 'permissions'" do
      it "denies access when group is non-collaborative and user lacks permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "permissions", params: { group_id: @non_collaborative_group.id }
        expect(response).to have_http_status(:unauthorized)
      end

      it "allows access when group is non-collaborative and user has permissions" do
        get "permissions", params: { group_id: @non_collaborative_group.id }
        expect(response).to be_successful
      end

      it "allows access when group is collaborative regardless of permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get "permissions", params: { group_id: @collaborative_group.id }
        expect(response).to be_successful
      end
    end

    describe "GET 'accept_invitation'" do
      it "returns unauthorized when accessing non_collaborative group without permissions" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        membership = @non_collaborative_group.invite_user(@user)
        get "accept_invitation", params: { group_id: @non_collaborative_group.id, uuid: membership.uuid }
        expect(response).to have_http_status(:unauthorized)
        json = response.parsed_body
        expect(json["message"]).to eq "Not authorized to manage differentiation tag."
      end
    end

    describe "GET #bulk_user_tags" do
      before do
        @student1 = User.create!(name: "Student 1")
        @student2 = User.create!(name: "Student 2")
        @tag1 = @course.group_categories.create!(name: "Tag 1", non_collaborative: true)
        @tag2 = @course.group_categories.create!(name: "Tag 2", non_collaborative: true)
        @other = @course.group_categories.create!(name: "Other", non_collaborative: false)
        @course.enroll_user(@teacher, "TeacherEnrollment")
        @course.enroll_user(@student1, "StudentEnrollment")
        @course.enroll_user(@student2, "StudentEnrollment")
        @tag1_group = @course.groups.create!(name: "Tag 1", group_category: @tag1)
        @tag2_group = @course.groups.create!(name: "Tag 2", group_category: @tag2)
        @other_group = @course.groups.create!(name: "Other", group_category: @other)
        @tag1_group.add_user(@student1)
        @tag1_group.add_user(@student2)
        @tag2_group.add_user(@student2)
        @other_group.add_user(@student1)
      end

      it "returns correct tag IDs for each user" do
        get :bulk_user_tags, params: { course_id: @course.id, user_ids: [@student1.id, @student2.id] }, format: :json
        expect(response).to have_http_status(:ok)
        json = response.parsed_body
        expect(json[@student1.id.to_s]).to match_array([@tag1_group.id])
        expect(json[@student2.id.to_s]).to match_array([@tag1_group.id, @tag2_group.id])
      end

      it "returns empty array for users with no tags" do
        user3 = User.create!(name: "No Tags")
        get :bulk_user_tags, params: { course_id: @course.id, user_ids: [user3.id] }, format: :json
        expect(response).to have_http_status(:ok)
        json = response.parsed_body
        expect(json[user3.id.to_s]).to eq([])
      end

      it "returns 403 if not authorized" do
        RoleOverride::GRANULAR_MANAGE_TAGS_PERMISSIONS.each do |permission|
          @course.account.role_overrides.create!(
            permission:,
            role: teacher_role,
            enabled: false
          )
        end
        get :bulk_user_tags,
            params: { course_id: @course.id, user_ids: [@student1.id] },
            format: :json
        expect(response)
          .to have_http_status(:forbidden)
          .or have_http_status(:unauthorized)
      end

      it "returns ok for empty user_ids" do
        get :bulk_user_tags, params: { course_id: @course.id, user_ids: [] }, format: :json
        expect(response).to have_http_status(:ok)
        json = response.parsed_body
        expect(json).to eq({})
      end
    end
  end
end
