require 'spec_helper'

module VCAP::CloudController
  RSpec.describe Membership do
    let(:user) { User.make }
    let!(:space) { Space.make(organization:) }
    let(:organization) { Organization.make }

    let(:membership) { Membership.new(user) }

    describe '#role_applies?' do
      context 'when space roles are provided' do
        before do
          organization.add_user(user)
        end

        describe 'SPACE_DEVELOPER' do
          context 'when the user has the role' do
            before do
              space.add_developer(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::SPACE_DEVELOPER, space.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              space.remove_developer(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::SPACE_DEVELOPER, space.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'SPACE_SUPPORTER' do
          context 'when the user has the role' do
            before do
              space.add_supporter(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::SPACE_SUPPORTER, space.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              space.remove_supporter(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::SPACE_SUPPORTER, space.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'SPACE_MANAGER' do
          context 'when the user has the role' do
            before do
              space.add_manager(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::SPACE_MANAGER, space.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              space.remove_manager(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::SPACE_MANAGER, space.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'SPACE_AUDITOR' do
          context 'when the user has the role' do
            before do
              space.add_auditor(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::SPACE_AUDITOR, space.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              space.remove_auditor(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::SPACE_AUDITOR, space.id)
              expect(result).to be_falsey
            end
          end
        end

        context 'when the user has any one of multiple requested roles' do
          before do
            space.add_manager(user)
            space.remove_developer(user)
            space.remove_auditor(user)
          end

          it 'returns true' do
            result = membership.role_applies?([
              Membership::SPACE_MANAGER,
              Membership::SPACE_DEVELOPER,
              Membership::SPACE_AUDITOR
            ], space.id)

            expect(result).to be_truthy
          end
        end

        context 'when the user has none of multiple requested roles' do
          before do
            space.remove_manager(user)
            space.remove_developer(user)
            space.remove_auditor(user)
          end

          it 'returns false' do
            result = membership.role_applies?([
              Membership::SPACE_MANAGER,
              Membership::SPACE_DEVELOPER,
              Membership::SPACE_AUDITOR
            ], space.id)

            expect(result).to be_falsey
          end
        end

        context 'when the space id is nil' do
          it 'returns false' do
            result = membership.role_applies?(Membership::SPACE_DEVELOPER)
            expect(result).to be_falsey
          end
        end

        context 'when the space is in a suspended org and the user has the required role' do
          before do
            space.add_developer(user)
            space.add_manager(user)
            space.add_auditor(user)
            organization.status = 'suspended'
            organization.save
            space.save
          end

          it 'returns true' do
            result = membership.role_applies?([
              Membership::SPACE_DEVELOPER,
              Membership::SPACE_MANAGER,
              Membership::SPACE_AUDITOR
            ],
                                              space.id)
            expect(result).to be_truthy
          end
        end
      end

      context 'when org roles are provided' do
        before do
          organization.add_user(user)
        end

        describe 'ORG_USER' do
          before do
            organization.add_user(user)
          end

          context 'when the user has the role' do
            before do
              organization.add_user(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::ORG_USER, nil, organization.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              organization.remove_user(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::ORG_USER, nil, organization.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'ORG_MANAGER' do
          context 'when the user has the role' do
            before do
              organization.add_manager(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::ORG_MANAGER, nil, organization.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              organization.remove_manager(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::ORG_MANAGER, nil, organization.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'ORG_AUDITOR' do
          context 'when the user has the role' do
            before do
              organization.add_auditor(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::ORG_AUDITOR, nil, organization.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              organization.remove_auditor(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::ORG_AUDITOR, nil, organization.id)
              expect(result).to be_falsey
            end
          end
        end

        describe 'ORG_BILLING_MANAGER' do
          context 'when the user has the role' do
            before do
              organization.add_billing_manager(user)
            end

            it 'returns true' do
              result = membership.role_applies?(Membership::ORG_BILLING_MANAGER, nil, organization.id)
              expect(result).to be_truthy
            end
          end

          context 'when the user does not have the role' do
            before do
              organization.remove_billing_manager(user)
            end

            it 'returns false' do
              result = membership.role_applies?(Membership::ORG_BILLING_MANAGER, nil, organization.id)
              expect(result).to be_falsey
            end
          end
        end

        context 'when the user has any one of multiple requested roles' do
          before do
            organization.add_manager(user)
            organization.remove_billing_manager(user)
            organization.remove_auditor(user)
          end

          it 'returns true' do
            result = membership.role_applies?([
              Membership::ORG_MANAGER,
              Membership::ORG_BILLING_MANAGER,
              Membership::ORG_AUDITOR
            ], nil, organization.id)

            expect(result).to be_truthy
          end
        end

        context 'when the user has none of multiple requested roles' do
          before do
            organization.remove_manager(user)
            organization.remove_billing_manager(user)
            organization.remove_auditor(user)
          end

          it 'returns false' do
            result = membership.role_applies?([
              Membership::ORG_MANAGER,
              Membership::ORG_BILLING_MANAGER,
              Membership::ORG_AUDITOR
            ], nil, organization.id)

            expect(result).to be_falsey
          end
        end

        context 'when the org_guid is nil' do
          before do
            space.add_developer(user)
          end

          it 'returns false' do
            result = membership.role_applies?(Membership::ORG_USER, space.id, nil)
            expect(result).to be_falsey
          end
        end

        context 'when the org is suspended and the user has the required role' do
          before do
            organization.add_user(user)
            organization.add_manager(user)
            organization.add_billing_manager(user)
            organization.add_auditor(user)
            organization.status = 'suspended'
            organization.save
            space.save
          end

          it 'returns true' do
            result = membership.role_applies?([
              Membership::ORG_USER,
              Membership::ORG_MANAGER,
              Membership::ORG_AUDITOR,
              Membership::ORG_BILLING_MANAGER
            ],
                                              nil, organization.id)
            expect(result).to be_truthy
          end
        end
      end

      context 'when space and org roles are provided' do
        before do
          organization.add_user(user)
        end

        context 'when the user has any one of multiple requested roles' do
          before do
            space.add_manager(user)
            space.remove_developer(user)
            space.remove_auditor(user)
            organization.add_manager(user)
          end

          it 'returns true' do
            result = membership.role_applies?([
              Membership::ORG_MANAGER,
              Membership::SPACE_MANAGER,
              Membership::SPACE_DEVELOPER,
              Membership::SPACE_AUDITOR
            ],
                                              space.id, organization.id)

            expect(result).to be_truthy
          end
        end

        context 'when the user has none of multiple requested roles' do
          before do
            space.remove_manager(user)
            space.remove_developer(user)
            space.remove_auditor(user)
            organization.remove_manager(user)
          end

          it 'returns false' do
            result = membership.role_applies?([
              Membership::ORG_MANAGER,
              Membership::SPACE_MANAGER,
              Membership::SPACE_DEVELOPER,
              Membership::SPACE_AUDITOR
            ],
                                              space.id, organization.id)

            expect(result).to be_falsey
          end
        end
      end
    end

    describe '#authorized_org_guids' do
      context 'when the user has a role in the organization' do
        context 'for org users' do
          it 'returns all orgs in which the user is a member' do
            organization.add_user(user)
            guids = membership.authorized_org_guids(Membership::ORG_USER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for org auditors' do
          it 'returns all orgs in which the user is an auditor' do
            organization.add_auditor(user)
            guids = membership.authorized_org_guids(Membership::ORG_AUDITOR)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for org managers' do
          it 'returns all orgs in which the user is a manager' do
            organization.add_manager(user)
            guids = membership.authorized_org_guids(Membership::ORG_MANAGER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for org billing managers' do
          it 'returns all orgs in which the user is a billing manager' do
            organization.add_billing_manager(user)
            guids = membership.authorized_org_guids(Membership::ORG_BILLING_MANAGER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for space managers' do
          it 'returns all orgs for spaces in which the user is a space manager' do
            SpaceManager.create(user:, space:)
            guids = membership.authorized_org_guids(Membership::SPACE_MANAGER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for space developers' do
          it 'returns all orgs for spaces in which the user is a space developer' do
            SpaceDeveloper.create(user:, space:)
            guids = membership.authorized_org_guids(Membership::SPACE_DEVELOPER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for space supporters' do
          it 'returns all orgs for spaces in which the user is a space supporters' do
            SpaceSupporter.create(user:, space:)
            guids = membership.authorized_org_guids(Membership::SPACE_SUPPORTER)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'for space auditors' do
          it 'returns all orgs for spaces in which the user is a space auditor' do
            SpaceAuditor.create(user:, space:)
            guids = membership.authorized_org_guids(Membership::SPACE_AUDITOR)
            expect(guids).to eq([organization.guid])
          end
        end

        context 'when the user has multiple roles' do
          it 'does not return duplicate org guids' do
            organization.add_auditor(user)
            organization.add_billing_manager(user)

            guids = membership.authorized_org_guids([Membership::ORG_BILLING_MANAGER, Membership::ORG_AUDITOR])

            expect(guids).to eq([organization.guid])
          end
        end
      end

      context 'when the user has no role in any organization' do
        it 'returns an empty list' do
          guids = membership.authorized_org_guids([Membership::ORG_USER,
                                                   Membership::ORG_AUDITOR,
                                                   Membership::ORG_BILLING_MANAGER,
                                                   Membership::ORG_MANAGER])
          expect(guids).to be_empty
        end
      end
    end

    describe '#authorized_space_guids' do
      let(:user) { User.make }

      before do
        organization.add_user(user)
      end

      context 'space developers' do
        before do
          space.add_developer(user)
        end

        it 'returns all spaces in which the user develops' do
          guids = membership.authorized_space_guids(Membership::SPACE_DEVELOPER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'space managers' do
        before do
          space.add_manager(user)
        end

        it 'returns all spaces that the user managers' do
          guids = membership.authorized_space_guids(Membership::SPACE_MANAGER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'space auditors' do
        before do
          space.add_auditor(user)
        end

        it 'returns all spaces that the user audits' do
          guids = membership.authorized_space_guids(Membership::SPACE_AUDITOR)

          expect(guids).to eq([space.guid])
        end
      end

      context 'space supporter' do
        before do
          space.add_supporter(user)
        end

        it 'returns all spaces in which the user supports applications' do
          guids = membership.authorized_space_guids(Membership::SPACE_SUPPORTER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'org user' do
        it 'returns all spaces that the user is in the org' do
          guids = membership.authorized_space_guids(Membership::ORG_USER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'org manager' do
        before do
          organization.add_manager(user)
        end

        it 'returns all spaces that the user org manages' do
          guids = membership.authorized_space_guids(Membership::ORG_MANAGER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'org billing manager' do
        before do
          organization.add_billing_manager(user)
        end

        it 'returns all spaces that the user org billing manages' do
          guids = membership.authorized_space_guids(Membership::ORG_BILLING_MANAGER)

          expect(guids).to eq([space.guid])
        end
      end

      context 'org auditor' do
        before do
          organization.add_auditor(user)
        end

        it 'returns all spaces that the user org audits' do
          guids = membership.authorized_space_guids(Membership::ORG_AUDITOR)

          expect(guids).to eq([space.guid])
        end
      end

      context 'mix of org and space roles' do
        let(:org_managed) { Organization.make }
        let(:org_audited) { Organization.make }
        let!(:space1_in_managed_org) { Space.make(organization: org_managed) }
        let!(:space2_in_managed_org) { Space.make(organization: org_managed) }
        let!(:space_in_audited_org) { Space.make(organization: org_audited) }
        let!(:some_other_space) { Space.make }

        before do
          org_managed.add_manager(user)
          org_audited.add_auditor(user)
          space.add_developer(user)
          space.add_auditor(user)
        end

        it 'returns the correct space guids without duplicates' do
          guids = membership.authorized_space_guids([Membership::ORG_MANAGER, Membership::SPACE_DEVELOPER, Membership::SPACE_AUDITOR])

          expect(guids).to contain_exactly(space1_in_managed_org.guid, space2_in_managed_org.guid, space.guid)
        end
      end
    end
  end
end
