require "rails_helper"

describe Admin::BackupsController do

  it "is a subclass of AdminController" do
    expect(Admin::BackupsController < Admin::AdminController).to eq(true)
  end

  let(:backup_filename) { "2014-02-10-065935.tar.gz" }

  context "while logged in as an admin" do

    before { @admin = log_in(:admin) }

    describe ".index" do

      context "html format" do

        it "preloads important data" do
          Backup.expects(:all).returns([])
          subject.expects(:store_preloaded).with("backups", "[]")

          BackupRestore.expects(:operations_status).returns({})
          subject.expects(:store_preloaded).with("operations_status", "{}")

          BackupRestore.expects(:logs).returns([])
          subject.expects(:store_preloaded).with("logs", "[]")

          get :index, format: :html, xhr: true

          expect(response).to be_success
        end

      end

      context "json format" do

        it "returns a list of all the backups" do
          Backup.expects(:all).returns([Backup.new("backup1"), Backup.new("backup2")])

          get :index, format: :json, xhr: true

          expect(response).to be_success

          json = JSON.parse(response.body)
          expect(json[0]["filename"]).to eq("backup1")
          expect(json[1]["filename"]).to eq("backup2")
        end

      end

    end

    describe ".status" do

      it "returns the current backups status" do
        BackupRestore.expects(:operations_status)

        get :status, format: :json

        expect(response).to be_success
      end

    end

    describe ".create" do

      it "starts a backup" do
        BackupRestore.expects(:backup!).with(@admin.id, publish_to_message_bus: true, with_uploads: false, client_id: "foo")

        post :create, params: {
          with_uploads: false, client_id: "foo"
        }, format: :json

        expect(response).to be_success
      end

    end

    describe ".show" do

      it "uses send_file to transmit the backup" do
        begin
          token = EmailBackupToken.set(@admin.id)
          path = File.join(Backup.base_directory, backup_filename)
          File.open(path, "w") { |f| f.write("hello") }

          Backup.create_from_filename(backup_filename)

          StaffActionLogger.any_instance.expects(:log_backup_download).once

          get :show, params: { id: backup_filename, token: token }, format: :json

          expect(response.headers['Content-Length']).to eq("5")
          expect(response.headers['Content-Disposition']).to match(/attachment; filename/)
        ensure
          File.delete(path)
          EmailBackupToken.del(@admin.id)
        end
      end

      it "returns 422 when token is bad" do
        begin
          path = File.join(Backup.base_directory, backup_filename)
          File.open(path, "w") { |f| f.write("hello") }

          Backup.create_from_filename(backup_filename)

          get :show, params: { id: backup_filename, token: "bad_value" }, xhr: true

          expect(response.status).to eq(422)
        ensure
          File.delete(path)
        end
      end

      it "returns 404 when the backup does not exist" do
        token = EmailBackupToken.set(@admin.id)
        Backup.expects(:[]).returns(nil)

        get :show, params: { id: backup_filename, token: token }, format: :json

        EmailBackupToken.del(@admin.id)

        expect(response).to be_not_found
      end

    end

    describe ".destroy" do

      let(:b) { Backup.new(backup_filename) }

      it "removes the backup if found" do
        Backup.expects(:[]).with(backup_filename).returns(b)
        b.expects(:remove)

        StaffActionLogger.any_instance.expects(:log_backup_destroy).with(b).once

        delete :destroy, params: { id: backup_filename }, format: :json

        expect(response).to be_success
      end

      it "doesn't remove the backup if not found" do
        Backup.expects(:[]).with(backup_filename).returns(nil)
        b.expects(:remove).never
        delete :destroy, params: { id: backup_filename }, format: :json
        expect(response).not_to be_success
      end

    end

    describe ".logs" do

      it "preloads important data" do
        BackupRestore.expects(:operations_status).returns({})
        subject.expects(:store_preloaded).with("operations_status", "{}")

        BackupRestore.expects(:logs).returns([])
        subject.expects(:store_preloaded).with("logs", "[]")

        get :logs, format: :html, xhr: true

        expect(response).to be_success
      end
    end

    describe ".restore" do

      it "starts a restore" do
        expect(SiteSetting.disable_emails).to eq(false)
        BackupRestore.expects(:restore!).with(@admin.id, filename: backup_filename, publish_to_message_bus: true, client_id: "foo")

        post :restore, params: { id: backup_filename, client_id: "foo" }, format: :json

        expect(SiteSetting.disable_emails).to eq(true)
        expect(response).to be_success
      end

    end

    describe ".readonly" do

      it "enables readonly mode" do
        Discourse.expects(:enable_readonly_mode)

        expect { put :readonly, params: { enable: true }, format: :json }
          .to change { UserHistory.count }.by(1)

        expect(response).to be_success

        user_history = UserHistory.last

        expect(UserHistory.last.action).to eq(UserHistory.actions[:change_readonly_mode])
        expect(UserHistory.last.new_value).to eq('t')
      end

      it "disables readonly mode" do
        Discourse.expects(:disable_readonly_mode)

        expect { put :readonly, params: { enable: false }, format: :json }
          .to change { UserHistory.count }.by(1)

        expect(response).to be_success

        user_history = UserHistory.last

        expect(UserHistory.last.action).to eq(UserHistory.actions[:change_readonly_mode])
        expect(UserHistory.last.new_value).to eq('f')
      end

    end

    describe "#upload_backup_chunk" do
      describe "when filename contains invalid characters" do
        it "should raise an error" do
          ['灰色.tar.gz', '; echo \'haha\'.tar.gz'].each do |invalid_filename|
            described_class.any_instance.expects(:has_enough_space_on_disk?).returns(true)

            post :upload_backup_chunk, params: {
              resumableFilename: invalid_filename, resumableTotalSize: 1
            }

            expect(response.status).to eq(415)
            expect(response.body).to eq(I18n.t('backup.invalid_filename'))
          end
        end
      end

      describe "when filename is valid" do
        it "should upload the file successfully" do
          begin
            described_class.any_instance.expects(:has_enough_space_on_disk?).returns(true)

            filename = 'test_Site-0123456789.tar.gz'

            post :upload_backup_chunk, params: {
              resumableFilename: filename,
              resumableTotalSize: 1,
              resumableIdentifier: 'test',
              resumableChunkNumber: '1',
              resumableChunkSize: '1',
              resumableCurrentChunkSize: '1',
              file: fixture_file_upload(Tempfile.new)
            }, format: :json

            expect(response.status).to eq(200)
            expect(response.body).to eq("")
          ensure
            File.delete(File.join(Backup.base_directory, filename))
          end
        end
      end
    end

  end

end
