#  Copyright (c) 2012-2025, Jungwacht Blauring Schweiz. This file is part of
#  hitobito and licensed under the Affero General Public License version 3
#  or later. See the COPYING file at the top-level directory or at
#  https://github.com/hitobito/hitobito.

require "spec_helper"

describe PersonAbility do
  subject { ability }

  let(:ability) { Ability.new(role.person.reload) }

  context :layer_and_below_full do
    let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

    before do
      Group::TopGroup::Leader.permissions = [:layer_and_below_full]
    end

    after do
      Group::TopGroup::Leader.permissions = [:admin, :finance, :layer_and_below_full, :contact_data, :impersonation]
    end

    it "may modify any public role in lower layers" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:security, other.person)
    end

    it "may not update root email if in same group" do
      root = people(:root)
      Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group), person: root)
      is_expected.to be_able_to(:update, root.reload)
      is_expected.not_to be_able_to(:update_email, root)
    end

    it "may modify its role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may modify its password" do
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may modify its security" do
      is_expected.to be_able_to(:security, role.person)
    end

    it "may modify externals in the same layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_layer))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
    end

    it "may not view any non-visible in lower layers" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any externals in lower layers" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may index groups in lower layer" do
      is_expected.to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may show notes, tags and security in same layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
      is_expected.to be_able_to(:security, other.person)
    end

    it "may show notes, tags and security in lower layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
      is_expected.to be_able_to(:security, other.person)
    end

    it "may show person with ended role in layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    # rubocop:todo Layout/LineLength
    it "may show person with ended role in layer even if outside configured period because it may show deleted people" do
      # rubocop:enable Layout/LineLength
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 3.months.ago,
        end_on: 2.months.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in lower layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not show person with ended role in different layer" do
      other = Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: Group::TopLayer.create(name: "foo"),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may create households" do
      is_expected.to be_able_to(:create_households, Person)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  describe "layer_and_below_full in bottom layer" do
    let(:role) { Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one)) }

    it "may create other users" do
      is_expected.to be_able_to(:create, Person)
    end

    it "may modify its role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may modify any public role in same layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym,
        group: groups(:bottom_layer_one),
        person: Fabricate(:person, password: "foobarfoobar", password_confirmation: "foobarfoobar"))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
      # is_expected.to be_able_to(:security, other)
    end

    it "may not view any public role in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
      is_expected.not_to be_able_to(:security, other)
    end

    it "may not update email for person with role in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may not view any public role in other layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not update email for person with role in other layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may update email for person with role in other layer if layer_and_below_full there" do
      Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two), person: role.person)
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with role in other group if group_full there" do
      Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_two_one), person: role.person)
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_two_one))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with uncapable role in upper layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with uncapable role in other layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for uncapable person with uncapable role in other layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may not update email for uncapable person with role in other layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may modify externals in his layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
    end

    it "may modify children in his layer" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
      is_expected.to be_able_to(:security, other.person)
    end

    it "may not view any externals in upper layers" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may index groups in upper layer" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may show notes and tags in same layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may show notes and tags in lower group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may not show notes and tags in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:index_tags, other.person.reload)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may show person with ended role in upper layer" do
      other = Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may create households" do
      is_expected.to be_able_to(:create_households, Person)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  context :layer_and_below_read do
    # member with additional group_full role
    let(:role) { Fabricate(Group::TopGroup::Secretary.name.to_sym, group: groups(:top_group)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may modify himself" do
      is_expected.to be_able_to(:update, role.person.reload)
    end

    it "may modify its read role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may create other users as group admin" do
      is_expected.to be_able_to(:create, Person)
    end

    it "may view any public role in same layer" do
      other = Fabricate(Group::GlobalGroup::Member.name.to_sym, group: groups(:toppers))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify any role in same layer" do
      other = Fabricate(Group::GlobalGroup::Member.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may view any externals in same layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:toppers))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may modify any role in same group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update, other)
    end

    it "may view any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any externals in groups below" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index groups in lower layer" do
      is_expected.to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:toppers))
      is_expected.to be_able_to(:index_full_people, groups(:toppers))
      is_expected.to be_able_to(:index_local_people, groups(:toppers))
    end

    it "may not show person with ended role in layer" do
      other = Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in layer if configured" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in lower layer if configured" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not show person with ended role in layer if outside configured period" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group), start_on: 2.years.ago,
        end_on: 2.months.ago)
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    context "without any writing permission" do
      let(:role) { Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one)) }

      it "may not query people" do
        is_expected.not_to be_able_to(:query, Person)
      end
    end
  end

  context :layer_full do
    let(:role) { Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group)) }

    it "may not modify any public role in lower layers" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not update root email if in same group" do
      root = people(:root)
      Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group), person: root)
      is_expected.to be_able_to(:update, root.reload)
      is_expected.not_to be_able_to(:update_email, root)
    end

    it "may modify its role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may modify its password" do
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may modify externals in the same layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_layer))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
    end

    it "may not view any non-visible in lower layers" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
      is_expected.not_to be_able_to(:show_full, other.person)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any non-contact data in lower layers" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any externals in lower layers" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
      is_expected.not_to be_able_to(:show_full, other.person)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not index groups in lower layer" do
      is_expected.not_to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may show notes and tags in same layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may not show notes and tags in lower layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_notes, other.person.reload)
      is_expected.not_to be_able_to(:index_tags, other.person)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may show person with ended role in layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not show person with ended role in lower layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  describe "layer_full in bottom layer" do
    let(:role) { Fabricate(Group::BottomLayer::LocalGuide.name.to_sym, group: groups(:bottom_layer_one)) }

    it "may create other users" do
      is_expected.to be_able_to(:create, Person)
    end

    it "may modify its role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may modify any public role in same layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym,
        group: groups(:bottom_layer_one),
        person: Fabricate(:person, password: "foobarfoobar", password_confirmation: "foobarfoobar"))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
      is_expected.to be_able_to(:security, other.person)
    end

    it "may not view any public role in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not update email for person with role in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may not view any public role in other layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any private role in other layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_two))
      is_expected.not_to be_able_to(:show, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not update email for person with role in other layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may update email for person with role in other layer if layer_full there" do
      Fabricate(Group::BottomLayer::LocalGuide.name.to_sym, group: groups(:bottom_layer_two), person: role.person)
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with role in other group if group_full there" do
      Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_two_one), person: role.person)
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_two_one))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with uncapable role in upper layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with uncapable role in other layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for uncapable person with uncapable role in other layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may not update email for uncapable person with role in other layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_two))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may modify externals in his layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
    end

    it "may modify children in his layer" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:create, other)
      is_expected.to be_able_to(:destroy, other)
    end

    it "may not view any externals in upper layers" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
      is_expected.not_to be_able_to(:security, other.person)
    end

    it "may index groups in upper layer" do
      is_expected.not_to be_able_to(:index_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may show notes and tags in same layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may show notes and tags in lower group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_notes, other.person.reload)
      is_expected.to be_able_to(:index_tags, other.person)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may not show notes and tags in upper layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:index_notes, other.person.reload)
      is_expected.not_to be_able_to(:index_tags, other.person)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may not show person with ended role in layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may create households" do
      is_expected.to be_able_to(:create_households, Person)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  context :layer_read do
    let(:role) { Fabricate(Group::TopGroup::LocalSecretary.name.to_sym, group: groups(:top_group)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may modify himself" do
      is_expected.to be_able_to(:update, role.person.reload)
    end

    it "may modify its read role" do
      is_expected.not_to be_able_to(:update, role)
    end

    it "may not destroy its role" do
      is_expected.not_to be_able_to(:destroy, role)
    end

    it "may create other users as group admin" do
      is_expected.not_to be_able_to(:create, Person)
    end

    it "may view any public role in same layer" do
      other = Fabricate(Group::GlobalGroup::Member.name.to_sym, group: groups(:toppers))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify any role in same layer" do
      other = Fabricate(Group::GlobalGroup::Member.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may view any externals in same layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:toppers))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify any role in same group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may not modify any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any externals in groups below" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index groups in lower layer" do
      is_expected.not_to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may index people same layer" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may index people in groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:toppers))
      is_expected.to be_able_to(:index_full_people, groups(:toppers))
      is_expected.to be_able_to(:index_local_people, groups(:toppers))
    end

    it "may not show notes and tags in same layer" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:index_notes, other.person.reload)
      is_expected.not_to be_able_to(:index_tags, other.person)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may not show notes and tags in lower layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_notes, other.person.reload)
      is_expected.not_to be_able_to(:index_tags, other.person)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may not show person with ended role in layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.to_not be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in layer if configured" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 2.weeks.ago,
        end_on: 1.week.ago)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not show person with ended role in layer if outside configured period" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group), start_on: 3.months.ago,
        end_on: 2.months.ago)
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may not show person with ended role in lower layer if configured" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one),
        start_on: 2.weeks.ago, end_on: 1.week.ago)
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may not create households" do
      is_expected.to_not be_able_to(:create_households, Person)
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end
  end

  context :contact_data do
    let(:role) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may modify himself" do
      is_expected.to be_able_to(:update, role.person.reload)
    end

    it "may not modify his role" do
      is_expected.not_to be_able_to(:update, role)
    end

    it "may not create other users" do
      is_expected.not_to be_able_to(:create, Person)
    end

    it "may view others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may view details of others in same group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show_details, other.person.reload)
    end
    it "may not view full of others in same group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify others in same group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may show any public role in same layer" do
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: groups(:toppers))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not view details of public role in same layer" do
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may not modify any role in same layer" do
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view externals in other group of same layer" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may view any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not modify any public role in groups below" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update, other)
    end

    it "may not view any externals in groups below" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index own group" do
      is_expected.to be_able_to(:index_people, groups(:top_group))
      is_expected.to be_able_to(:index_local_people, groups(:top_group))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_group))
    end

    it "may index groups anywhere" do
      is_expected.to be_able_to(:index_people, groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_group_one_one))
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end
  end

  context :group_and_below_full do
    let(:role) { Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may update himself" do
      is_expected.to be_able_to(:update, role.person.reload)
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may update her email with password" do
      himself = role.person.reload
      himself.encrypted_password = "foooo"
      is_expected.to be_able_to(:update_email, himself)
    end

    it "may update his role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may create other users" do
      is_expected.to be_able_to(:create, Person)
    end

    it "may view and update others in same group" do
      other = Fabricate(:person, password: "foobarfoobar", password_confirmation: "foobarfoobar")
      Fabricate(Role::External.name.to_sym, group: groups(:top_layer), person: other)
      is_expected.to be_able_to(:show, other.reload)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:update_email, other)
    end

    it "may update email for person in below group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may not view and update email for person in below layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:show, other.person.reload)
      is_expected.not_to be_able_to(:update, other.person)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may not update email for person in same group and in below layer" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      Fabricate(Role::External.name.to_sym, group: groups(:top_layer), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may update email for person in below group if group_and_below_full everywhere" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_layer))
      Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may not update root email if in same group" do
      root = people(:root)
      Fabricate(Role::External.name.to_sym, group: groups(:top_layer), person: root)
      is_expected.to be_able_to(:update, root.reload)
      is_expected.not_to be_able_to(:update_email, root)
    end

    it "may view and update externals in below group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show, other.person.reload)
      is_expected.to be_able_to(:update, other.person)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may view details of others in below group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show_details, other.person.reload)
    end

    it "may view full of others in below group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may index same group" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.to be_able_to(:index_local_people, groups(:top_layer))
      is_expected.to be_able_to(:index_full_people, groups(:top_layer))
    end

    it "may index below group" do
      is_expected.to be_able_to(:index_people, groups(:top_group))
      is_expected.to be_able_to(:index_local_people, groups(:top_group))
      is_expected.to be_able_to(:index_full_people, groups(:top_group))
    end

    it "may not index groups in below layer" do
      is_expected.not_to be_able_to(:index_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_layer_one))
    end

    it "may index and manage tags in same group" do
      other = Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer))
      is_expected.to be_able_to(:index_tags, other.person.reload)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may index and manage tags in below group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:index_tags, other.person.reload)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may not index or manage tags in below layer" do
      other = Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_tags, other.person.reload)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may create households" do
      is_expected.to be_able_to(:create_households, Person)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  context :group_and_below_read do
    let(:role) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may update himself" do
      is_expected.to be_able_to(:update, role.person.reload)
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may not update his role" do
      is_expected.not_to be_able_to(:update, role)
    end

    it "may not create other users" do
      is_expected.not_to be_able_to(:create, Person)
    end

    it "may view others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may view others in below group" do
      below = Fabricate(Group::GlobalGroup.name, parent: role.group)
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: below)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may not view others in same layer" do
      other = Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may view externals in same group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may view externals in below group" do
      below = Fabricate(Group::GlobalGroup.name, parent: role.group)
      other = Fabricate(Role::External.name.to_sym, group: below)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may view details of others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.to be_able_to(:show_details, other.person.reload)
    end

    it "may view details of others in below group" do
      below = Fabricate(Group::GlobalGroup.name, parent: role.group)
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: below)
      is_expected.to be_able_to(:show_details, other.person.reload)
    end

    it "may not view full of others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may not view full of others in same group" do
      below = Fabricate(Group::GlobalGroup.name, parent: role.group)
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: below)
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may index same group" do
      is_expected.to be_able_to(:index_people, groups(:top_group))
      is_expected.to be_able_to(:index_local_people, groups(:top_group))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_group))
    end

    it "may index below group" do
      below = Fabricate(Group::GlobalGroup.name, parent: role.group)
      is_expected.to be_able_to(:index_people, below)
      is_expected.to be_able_to(:index_local_people, below)
      is_expected.not_to be_able_to(:index_full_people, below)
    end

    it "may not index groups in same layer" do
      is_expected.to be_able_to(:index_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may not index and manage tags in same group" do
      other = Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer))
      is_expected.not_to be_able_to(:index_tags, other.person.reload)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may not index and manage tags in below group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:index_tags, other.person.reload)
      is_expected.not_to be_able_to(:manage_tags, other.person)
    end

    it "may not create households" do
      is_expected.to_not be_able_to(:create_households, Person)
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end
  end

  context :group_full do
    let(:role) { Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may update himself" do
      is_expected.to be_able_to(:update, role.person.reload)
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may update her email with password" do
      himself = role.person.reload
      himself.encrypted_password = "foooo"
      is_expected.to be_able_to(:update_email, himself)
    end

    it "may update his role" do
      is_expected.to be_able_to(:update, role)
    end

    it "may create other users" do
      is_expected.to be_able_to(:create, Person)
    end

    it "may view and update others in same group" do
      other = Fabricate(:person, password: "foobarfoobar", password_confirmation: "foobarfoobar")
      Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one), person: other)
      is_expected.to be_able_to(:show, other.reload)
      is_expected.to be_able_to(:update, other)
      is_expected.to be_able_to(:update_email, other)
    end

    it "may not update email for person in other group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_two), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may update email for person in other group if group_full everywhere" do
      Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_two), person: role.person)
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_two), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for person with uncapable role in other group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_two), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may update email for uncapable person with uncapable role in other group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one))
      Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_two), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may not update email for uncapable person with role in other group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one))
      Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_two), person: other.person)
      is_expected.to be_able_to(:update, other.person.reload)
      is_expected.not_to be_able_to(:update_email, other.person)
    end

    it "may not update root email if in same group" do
      root = people(:root)
      Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one), person: root)
      is_expected.to be_able_to(:update, root.reload)
      is_expected.not_to be_able_to(:update_email, root)
    end

    it "may view and update externals in same group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show, other.person.reload)
      is_expected.to be_able_to(:update, other.person)
      is_expected.to be_able_to(:update_email, other.person)
    end

    it "may view details of others in same group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show_details, other.person.reload)
    end

    it "may view full of others in same group" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show_full, other.person.reload)
    end

    it "may not view public role in same layer" do
      other = Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index same group" do
      is_expected.to be_able_to(:index_people, groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_local_people, groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_full_people, groups(:bottom_group_one_one))
    end

    it "may not index groups in same layer" do
      is_expected.not_to be_able_to(:index_people, groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_group_one_two))
    end

    it "may index and manage tags in same group" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_tags, other.person.reload)
      is_expected.to be_able_to(:manage_tags, other.person)
    end

    it "may create households" do
      is_expected.to be_able_to(:create_households, Person)
    end

    it "may query people" do
      is_expected.to be_able_to(:query, Person)
    end
  end

  context :group_read do
    let(:role) { Fabricate(Group::BottomGroup::Member.name.to_sym, group: groups(:bottom_group_one_one)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may update himself" do
      is_expected.to be_able_to(:update, role.person.reload)
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may not update his role" do
      is_expected.not_to be_able_to(:update, role)
    end

    it "may not create other users" do
      is_expected.not_to be_able_to(:create, Person)
    end

    it "may view others in same group" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in group" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one),
        end_on: Time.zone.yesterday)
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in group if configured" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one),
        end_on: Time.zone.yesterday)
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may show person with ended role in group if outside configured period" do
      allow(Settings.people).to receive(:ended_roles_readable_for).and_return(1.month)
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one),
        end_on: 2.months.ago)
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may view externals in same group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show, other.person.reload)
    end

    it "may view details of others in same group" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.to be_able_to(:show_details, other.person.reload)
    end

    it "may not view full of others in same group" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may not view public role in same layer" do
      other = Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index same group" do
      is_expected.to be_able_to(:index_people, groups(:bottom_group_one_one))
      is_expected.to be_able_to(:index_local_people, groups(:bottom_group_one_one))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_group_one_one))
    end

    it "may not index groups in same layer" do
      is_expected.not_to be_able_to(:index_people, groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:index_full_people, groups(:bottom_group_one_two))
      is_expected.not_to be_able_to(:index_local_people, groups(:bottom_group_one_two))
    end

    it "may not create households" do
      is_expected.to_not be_able_to(:create_households, Person)
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end
  end

  context "finance" do
    let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

    it "may not index in bottom layer group" do
      other = Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one))
      is_expected.not_to be_able_to(:index_invoices, other)
    end

    it "may index in top group" do
      other = Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:index_invoices, other)
    end
  end

  context "impersonation" do
    let(:role) { people(:top_leader).roles.first }

    it "may not impersonate user" do
      is_expected.to be_able_to(:impersonate_user, people(:bottom_member))
    end
  end

  describe "no permissions" do
    let(:role) { Fabricate(Role::External.name.to_sym, group: groups(:top_group)) }

    it "may view details of himself" do
      is_expected.to be_able_to(:show_full, role.person.reload)
    end

    it "may view invoices of himself" do
      is_expected.to be_able_to(:index_invoices, role.person.reload)
    end

    it "may modify himself" do
      is_expected.to be_able_to(:update, role.person.reload)
      is_expected.to be_able_to(:update_email, role.person)
    end

    it "may not modify his role" do
      is_expected.not_to be_able_to(:update, role)
    end

    it "may not create other users" do
      is_expected.not_to be_able_to(:create, Person)
    end

    it "may not view others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may not view externals in same group" do
      other = Fabricate(Role::External.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may not view details of others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_details, other.person.reload)
    end

    it "may not view full of others in same group" do
      other = Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group))
      is_expected.not_to be_able_to(:show_full, other.person.reload)
    end

    it "may not view public role in same layer" do
      other = Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: groups(:toppers))
      is_expected.not_to be_able_to(:show, other.person.reload)
    end

    it "may index same group" do
      is_expected.not_to be_able_to(:index_people, groups(:top_group))
      is_expected.not_to be_able_to(:index_local_people, groups(:top_group))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_group))
    end

    it "may not index groups in same layer" do
      is_expected.not_to be_able_to(:index_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_full_people, groups(:top_layer))
      is_expected.not_to be_able_to(:index_local_people, groups(:top_layer))
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end
  end

  describe "root" do
    let(:user) { people(:root) }
    let(:ability) { Ability.new(user) }

    it "may not change her email" do
      is_expected.not_to be_able_to(:update_email, user)
    end
  end

  describe "people filter" do
    context "root layer and below full" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      context "in group from same layer" do
        let(:group) { groups(:top_group) }

        it "may create people filters" do
          is_expected.to be_able_to(:create, group.people_filters.new)
        end
      end

      context "in group from lower layer" do
        let(:group) { groups(:bottom_layer_one) }

        it "may create people filters" do
          is_expected.to be_able_to(:create, group.people_filters.new)
        end

        it "may define new people filters" do
          is_expected.to be_able_to(:new, group.people_filters.new)
        end
      end
    end

    context "bottom layer and below full" do
      let(:role) { Fabricate(Group::BottomLayer::Leader.name.to_sym, group: groups(:bottom_layer_one)) }

      context "in group from same layer" do
        let(:group) { groups(:bottom_layer_one) }

        it "may create people filters" do
          is_expected.to be_able_to(:create, group.people_filters.new)
        end
      end

      context "in group from upper layer" do
        let(:group) { groups(:top_layer) }

        it "may not create people filters" do
          is_expected.not_to be_able_to(:create, group.people_filters.new)
        end

        it "may define new people filters" do
          is_expected.to be_able_to(:new, group.people_filters.new)
        end
      end
    end

    context "layer and below read" do
      let(:role) { Fabricate(Group::TopGroup::Secretary.name.to_sym, group: groups(:top_group)) }

      context "in group from same layer" do
        let(:group) { groups(:top_group) }

        it "may not create people filters" do
          is_expected.not_to be_able_to(:create, group.people_filters.new)
        end

        it "may define new people filters" do
          is_expected.to be_able_to(:new, group.people_filters.new)
        end
      end

      context "in group from lower layer" do
        let(:group) { groups(:bottom_layer_one) }

        it "may not create people filters" do
          is_expected.not_to be_able_to(:create, group.people_filters.new)
        end

        it "may define new people filters" do
          is_expected.to be_able_to(:new, group.people_filters.new)
        end
      end
    end
  end

  context :show_details do
    let(:other) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)).person.reload }

    context "layer and below full" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      it "can show_details" do
        is_expected.to be_able_to(:show_details, other)
        is_expected.to be_able_to(:show_full, other)
      end
    end

    context "same group" do
      let(:role) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)) }

      it "can show_details" do
        is_expected.to be_able_to(:show_details, other)
        is_expected.not_to be_able_to(:show_full, other)
      end
    end

    context "group below" do
      let(:role) { Fabricate(Group::BottomGroup::Leader.name.to_sym, group: groups(:bottom_group_one_one)) }

      it "cannot show_details" do
        is_expected.not_to be_able_to(:show_details, other)
        is_expected.not_to be_able_to(:show_full, other)
      end
    end
  end

  context :send_password_instructions do
    let(:other) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)).person.reload }

    context "layer and below full" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      it "can send_password_instructions" do
        is_expected.to be_able_to(:send_password_instructions, other)
      end

      it "can send_password_instructions for external role" do
        external = Fabricate(Role::External.name.to_sym, group: groups(:top_group)).person.reload
        is_expected.to be_able_to(:send_password_instructions, external)
      end

      it "cannot send_password_instructions for self" do
        is_expected.not_to be_able_to(:send_password_instructions, role.person.reload)
      end
    end

    context "same group" do
      let(:role) { Fabricate(Group::TopGroup::Member.name.to_sym, group: groups(:top_group)) }

      it "cannot send_password_instructions" do
        is_expected.not_to be_able_to(:send_password_instructions, other)
      end
    end

    context "group below" do
      let(:role) { Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one)) }

      it "cannot send_password_instructions" do
        is_expected.not_to be_able_to(:send_password_instructions, other)
      end
    end
  end

  context :totp_reset do
    context "as admin" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      it "can reset other person" do
        is_expected.to be_able_to(:totp_reset, people(:bottom_member))
      end
    end

    context "as non admin" do
      let(:role) { Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one)) }

      it "can not reset other person" do
        is_expected.to_not be_able_to(:totp_reset, people(:bottom_member))
      end
    end

    context "layer and below full" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      it "can reset other person" do
        is_expected.to be_able_to(:totp_reset, people(:bottom_member))
      end
    end

    context "layer full" do
      let(:role) { Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group)) }

      it "can reset other person" do
        Fabricate(Group::TopGroup::Member.name.to_sym, person: people(:bottom_member), group: groups(:top_group))
        is_expected.to be_able_to(:totp_reset, people(:bottom_member))
      end
    end
  end

  context :totp_disable do
    context "as admin" do
      let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

      it "can not disable other person when forced" do
        expect(people(:bottom_member).roles.first.class)
          .to receive(:two_factor_authentication_enforced)
          .and_return(true)

        is_expected.to_not be_able_to(:totp_disable, people(:bottom_member))
      end

      it "can disable other person" do
        is_expected.to be_able_to(:totp_disable, people(:bottom_member))
      end
    end

    context "as non admin" do
      let(:role) { Fabricate(Group::BottomLayer::Member.name.to_sym, group: groups(:bottom_layer_one)) }

      it "can not disable other person" do
        is_expected.to_not be_able_to(:totp_disable, people(:bottom_member))
      end
    end
  end

  context "person without roles" do
    let(:person_without_roles) do
      person = Fabricate(Person.name.downcase.to_sym)
      person.update!(primary_group: groups(:top_layer))
      person
    end
    let(:ability) { Ability.new(person_without_roles) }

    it "may show_full himself" do
      is_expected.to be_able_to(:show_full, person_without_roles)
    end

    it "may not show other person" do
      is_expected.not_to be_able_to(:show, people(:bottom_member))
      is_expected.not_to be_able_to(:show, people(:top_leader))
    end

    it "may update_password if already set" do
      person_without_roles.update!(password: "exampleexample")
      is_expected.to be_able_to(:update_password, person_without_roles)
    end

    it "may not update_password if not already set" do
      is_expected.to_not be_able_to(:update_password, person_without_roles)
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end

    context "with an event leader role" do
      let!(:event_role) do
        participation = Fabricate(:event_participation, event: events(:top_event), participant: person_without_roles)
        Event::Role::Leader.create(participation:)
      end

      it "may query people" do
        is_expected.to be_able_to(:query, Person)
      end
    end

    context "with an event participant role" do
      let!(:event_role) do
        participation = Fabricate(:event_participation, event: events(:top_event), participant: person_without_roles)
        Event::Role::Participant.create(participation:)
      end

      it "may not query people" do
        is_expected.not_to be_able_to(:query, Person)
      end
    end
  end

  context "person with only basic permissions role" do
    let(:role) do
      Fabricate(Group::BottomLayer::BasicPermissionsOnly.sti_name.to_sym,
        group: groups(:bottom_layer_one))
    end

    it "may not show_details herself" do
      is_expected.to_not be_able_to(:show_details, role.person)
    end

    it "may not show_full herself" do
      is_expected.to_not be_able_to(:show_full, role.person)
    end

    it "may not show history on herself" do
      is_expected.to_not be_able_to(:history, role.person)
    end

    it "may not show log on herself" do
      is_expected.to_not be_able_to(:log, role.person)
    end

    it "may not index invoices on herself" do
      is_expected.to_not be_able_to(:index_invoices, role.person)
    end

    it "may not query people" do
      is_expected.not_to be_able_to(:query, Person)
    end

    it "may modify its security" do
      is_expected.to be_able_to(:security, role.person)
    end
  end
end
