# frozen_string_literal: true

require_relative '../spec_helper'

module Aws
  module S3
    describe Bucket do
      describe '#delete!' do
        let(:client) { S3::Client.new(stub_responses: true) }

        let(:bucket) { Bucket.new('bucket-name', client: client) }

        let(:list_object_versions_request) do
          {
            versions: [
              { key: 'key', version_id: 'vid-1' },
              { key: 'key', version_id: 'vid-3' },
              { key: 'key', version_id: 'vid-5' }
            ],
            delete_markers: [
              { key: 'key', version_id: 'vid-2' },
              { key: 'key', version_id: 'vid-4' }
            ]
          }
        end

        let(:delete_bucket_request) do
          {
            bucket: 'bucket-name',
            delete: {
              objects: match_array(
                [
                  { key: 'key', version_id: 'vid-1' },
                  { key: 'key', version_id: 'vid-2' },
                  { key: 'key', version_id: 'vid-3' },
                  { key: 'key', version_id: 'vid-4' },
                  { key: 'key', version_id: 'vid-5' }
                ]
              )
            }
          }
        end

        context 'the bucket deletes without failure' do
          it 'deletes object versions and then deletes the bucket' do
            client.stub_responses(
              :list_object_versions,
              list_object_versions_request
            )

            expect(client).to receive(:delete_objects)
              .with(delete_bucket_request)
              .and_call_original

            expect(client).to receive(:delete_bucket)
              .with({bucket: 'bucket-name'})
              .and_call_original

            bucket.delete!
          end
        end

        context 'the bucket raises a Errors::BucketNotEmpty exception' do
          let(:default_initial_wait) { 1.3 }
          let(:second_attempt_wait)  { default_initial_wait**2 }

          let(:bucket_not_empty_exception) do
            Errors::BucketNotEmpty.new nil, ''
          end

          it 'retries' do
            client.stub_responses(
              :list_object_versions,
              list_object_versions_request
            )

            expect(client).to receive(:delete_objects)
              .with(delete_bucket_request)
              .exactly(3).times

            expect(client).to receive(:delete_bucket)
              .with({bucket: 'bucket-name'})
              .and_raise(bucket_not_empty_exception).ordered

            expect(Kernel).to receive(:sleep).with(default_initial_wait).ordered

            expect(client).to receive(:delete_bucket)
              .with({bucket: 'bucket-name'})
              .and_raise(bucket_not_empty_exception).ordered

            expect(Kernel).to receive(:sleep).with(second_attempt_wait).ordered

            expect(client).to receive(:delete_bucket)
              .with({bucket: 'bucket-name'}).ordered
              .and_call_original

            bucket.delete!
          end

          context 'the :initial_wait option is set' do
            let(:initial_wait) { 3 }

            it 'respects it' do
              client.stub_responses(
                :list_object_versions,
                list_object_versions_request
              )

              expect(client).to receive(:delete_objects)
                .with(delete_bucket_request)
                .exactly(3).times

              expect(client).to receive(:delete_bucket)
                .with({bucket: 'bucket-name'})
                .and_raise(bucket_not_empty_exception).ordered

              expect(Kernel).to receive(:sleep).with(initial_wait).ordered

              expect(client).to receive(:delete_bucket)
                .with({bucket: 'bucket-name'})
                .and_raise(bucket_not_empty_exception).ordered

              expect(Kernel).to receive(:sleep).with(initial_wait**2).ordered

              expect(client).to receive(:delete_bucket)
                .with({bucket: 'bucket-name'}).ordered
                .and_call_original

              bucket.delete!(initial_wait: initial_wait)
            end
          end

          context 'the :max_attempts option is set' do
            let(:max_attempts) { 1 }

            it 'respects it' do
              client.stub_responses(
                :list_object_versions,
                list_object_versions_request
              )

              expect(client).to receive(:delete_objects)
                .with(delete_bucket_request)
                .and_call_original

              expect(client).to receive(:delete_bucket)
                .with({bucket: 'bucket-name'})
                .and_raise(bucket_not_empty_exception)

              expect do
                bucket.delete!(max_attempts: max_attempts)
              end.to raise_exception(bucket_not_empty_exception)
            end
          end
        end
      end
    end
  end
end
