require "spec_helper"

describe Imap::Backup::Serializer::Mbox do
  let(:stat) { double("File::Stat", mode: 0o700) }
  let(:base_path) { "/base/path" }
  let(:mbox_pathname) { "/base/path/my/folder.mbox" }
  let(:mbox_exists) { true }
  let(:imap_pathname) { "/base/path/my/folder.imap" }
  let(:imap_exists) { true }

  before do
    allow(Imap::Backup::Utils).to receive(:make_folder)
    allow(File).to receive(:exist?).with(base_path).and_return(true)
    allow(File).to receive(:stat).with(base_path).and_return(stat)
    allow(File).to receive(:exist?).with(mbox_pathname).and_return(mbox_exists)
    allow(File).to receive(:exist?).with(imap_pathname).and_return(imap_exists)
  end

  context "#initialize" do
    it "creates the containing directory" do
      described_class.new(base_path, "my/folder")

      expect(Imap::Backup::Utils).
        to have_received(:make_folder).with(base_path, "my", 0o700)
    end

    context "mbox and imap files" do
      context "if mbox exists and imap doesn't" do
        let(:imap_exists) { false }

        it "fails" do
          expect {
            described_class.new(base_path, "my/folder")
          }.to raise_error(RuntimeError, ".imap file missing")
        end
      end

      context "if imap exists and mbox doesn't" do
        let(:mbox_exists) { false }

        it "fails" do
          expect {
            described_class.new(base_path, "my/folder")
          }.to raise_error(RuntimeError, ".mbox file missing")
        end
      end
    end
  end

  context "instance methods" do
    let(:ids) { %w(3 2 1) }

    before do
      allow(CSV).to receive(:foreach) { |&b| ids.each { |id| b.call [id] } }
    end

    subject { described_class.new(base_path, "my/folder") }

    context "#uids" do
      it "returns the backed-up uids as sorted integers" do
        expect(subject.uids).to eq(ids.map(&:to_i).sort)
      end

      context "if the mbox does not exist" do
        let(:mbox_exists) { false }
        let(:imap_exists) { false }

        it "returns an empty Array" do
          expect(subject.uids).to eq([])
        end
      end
    end

    context "#save" do
      let(:mbox_formatted_message) { "message in mbox format" }
      let(:message_uid) { "999" }
      let(:message) do
        double("Email::Mboxrd::Message", to_serialized: mbox_formatted_message)
      end
      let(:mbox_file) { double("File - mbox", write: nil, close: nil) }
      let(:imap_file) { double("File - imap", write: nil, close: nil) }

      before do
        allow(Email::Mboxrd::Message).to receive(:new).and_return(message)
        allow(File).to receive(:open).with(mbox_pathname, "ab") { mbox_file }
        allow(File).to receive(:open).with(imap_pathname, "ab") { imap_file }
      end

      it "saves the message to the mbox" do
        subject.save(message_uid, "The\nemail\n")

        expect(mbox_file).to have_received(:write).with(mbox_formatted_message)
      end

      it "saves the uid to the imap file" do
        subject.save(message_uid, "The\nemail\n")

        expect(imap_file).to have_received(:write).with(message_uid + "\n")
      end

      context "when the message causes parsing errors" do
        before do
          allow(message).to receive(:to_serialized).and_raise(ArgumentError)
        end

        it "skips the message" do
          subject.save(message_uid, "The\nemail\n")
          expect(mbox_file).to_not have_received(:write)
        end

        it "does not fail" do
          expect do
            subject.save(message_uid, "The\nemail\n")
          end.to_not raise_error
        end
      end
    end
  end
end