require 'spec_helper'

describe SchleuderGitlabTicketing::List do
  let(:config){ SchleuderGitlabTicketing::Config.new('spec/fixtures/gitlab.yml') }
  let(:list1){ config.lists['test'] }
  describe 'basic' do
    context 'with a config' do
      it 'provides a way to process a mail' do
        expect(list1).to respond_to(:process)
      end
      it 'provides a way to check if it\'s configured properly' do
        expect(list1).to respond_to(:configured?)
      end
      it 'has a gitlab config' do
        expect(list1).to respond_to(:gitlab)
        expect(list1.gitlab).to be_an_instance_of(Gitlab::Client)
      end
      it 'loads the configured lists' do
        expect(config.lists.length).to eql(8)
        expect(config.lists.values.first).to be_instance_of(SchleuderGitlabTicketing::List)
      end
      it 'has sender filters' do
        expect(list1).to respond_to(:sender_filters)
        expect(list1.sender_filters).to be_a(Array)
        expect(list1.sender_filters.length).to eql(1)
      end
      it 'has subject filters' do
        expect(list1).to respond_to(:subject_filters)
        expect(list1.subject_filters).to be_a(Array)
        expect(list1.subject_filters.length).to eql(2)
      end
    end
  end
  context 'with talking to gitlab' do
    let(:gitlab) { double }
    context 'list properly configured?' do
      context 'with projects on gitlab' do
        before(:each) do
          allow_any_instance_of(SchleuderGitlabTicketing::List).to receive(:gitlab).and_return(gitlab)
          list1_project = double
          allow(list1_project).to receive(:name).and_return('tickets')
          allow(list1_project).to receive(:id).and_return(123)
          allow(list1_project).to receive_message_chain(:namespace,:name).and_return('support')
          list2_project = double
          allow(list2_project).to receive(:name).and_return('testing')
          allow(list2_project).to receive_message_chain(:namespace,:name).and_return('support')
          allow(gitlab).to receive(:search_projects).with('tickets').and_return([list1_project])
          allow(gitlab).to receive(:search_projects).with('testing').and_return([list2_project])
          allow(gitlab).to receive(:search_projects).with('tickets2').and_return([list2_project])
          allow_any_instance_of(Gitlab::Error::NotFound).to receive(:build_error_message).and_return('bla')
          allow(gitlab).to receive(:search_projects).with('tickets3').and_raise(Gitlab::Error::NotFound,'bla')
        end
        it 'labels correct lists properly' do
          expect(config.lists['test'].configured?).to be(true)
          expect(config.lists['test2'].configured?).to be(true)
          expect(config.lists['test_not_exist'].configured?).to be(false)
          expect(config.lists['test_not_exist2'].configured?).to be(false)
        end
      end
      context 'with missing config' do
        it 'fails' do
          expect(config.lists['invalid1'].configured?).to be(false)
          expect(config.lists['invalid2'].configured?).to be(false)
          expect(config.lists['invalid3'].configured?).to be(false)
          expect(config.lists['invalid4'].configured?).to be(false)
        end
      end
    end
    context 'processing mails' do
      before(:each) do
        allow_any_instance_of(SchleuderGitlabTicketing::List).to receive(:gitlab).and_return(gitlab)
        list1_project = double
        allow(list1_project).to receive(:name).and_return('tickets')
        allow(list1_project).to receive(:id).and_return(123)
        allow(list1_project).to receive_message_chain(:namespace,:name).and_return('support')
        allow(list1_project).to receive_message_chain(:namespace,:id).and_return(91)
        allow(gitlab).to receive(:search_projects).with('tickets').and_return([list1_project])
      end
      context 'ignores spam' do
        it 'drops spam' do
          m = MailStub.new('bla@example.com','Some subject',{ 'X-Spam-Flag' => 'yes' })
          expect(list1.process(m)).to be(false)
        end
        it 'drops spam case-insensitiv' do
          m = MailStub.new('bla@example.com','Some subject',{ 'X-Spam-Flag' => 'YES' })
          expect(list1.process(m)).to be(false)
        end
      end
      context 'ignores certain senders' do
        it 'by regexp' do
          m = MailStub.new('test@example.com','Some subject')
          expect(list1.process(m)).to be(false)
        end
      end
      context 'ignores certain subjects' do
        it 'by regexp' do
          m = MailStub.new('test2@example.com','Some subject - ignore me')
          expect(list1.process(m)).to be(false)
        end
        it 'by regexp 2' do
          m = MailStub.new('test2@example.com','[announce] exciting')
          expect(list1.process(m)).to be(false)
        end
        it 'works if there is no subject' do
          m = MailStub.new('test2@example.com',nil)
          expect(list1.send(:ignore_mail?,m)).to be(false)
        end
      end
      context 'with a proper list' do
        it 'creates a new ticket' do
          m = MailStub.new('user@example.com','question')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'question',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question"}).and_return(new_ticket)
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'creates a new ticket with an empty subject' do
          m = MailStub.new('user@example.com',nil)
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'Request from user@example.com',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: [not set]"}).and_return(new_ticket)
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('[gl/tickets#42]')
        end
        it 'creates a new ticket with a nearly empty subject' do
          m = MailStub.new('user@example.com','[ok]')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'Request from user@example.com',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: [ok]"}).and_return(new_ticket)
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'close', labels: '' })
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('[ok] [gl/tickets#42]')
        end
        it 'creates a new ticket with a nearly empty subject 2' do
          m = MailStub.new('user@example.com','[<test] [ok]')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'Request from user@example.com',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: [<test] [ok]"}).and_return(new_ticket)
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'close', labels: '' })
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('[<test] [ok] [gl/tickets#42]')
        end
        it 'creates a new ticket if ticket nr in subject can\'t be found' do
          m = MailStub.new('user@example.com','question [gl/tickets#66]')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:issue).with(123,66).and_return(nil)
          expect(gitlab).to receive(:create_issue).with(123,'question',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#66]"}).and_return(new_ticket)
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'creates a new ticket and does not assign it even if from member' do
          m = MailStub.new('member@example.com','question')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'question',{ description: "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question"}).and_return(new_ticket)
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'creates a new ticket and put it into close if subject indicates' do
          m = MailStub.new('user@example.com','question [ok]')
          expect(gitlab).not_to receive(:user_search)
          new_ticket = double
          expect(new_ticket).to receive(:iid).and_return(42)
          expect(new_ticket).to receive(:state).and_return('open')
          expect(gitlab).to receive(:create_issue).with(123,'question',{ description: "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [ok]"}).and_return(new_ticket)
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'close', labels: '' })
          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [ok] [gl/tickets#42]')
        end

        it 'updates an existing ticket but does not put it in process as it is from a user' do
          m = MailStub.new('user@example.com','question [gl/tickets#42]')
          expect(gitlab).to receive(:user_search).with('user@example.com').and_return([])
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).and_return([])
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end

        it 'updates an existing ticket and assignes it to the member and puts it into inprocess' do
          m = MailStub.new('member@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(99)
          expect(gitlab).to receive(:user_search).with('member@example.com').and_return([user])
          expect(gitlab).to receive(:team_member).with(123,99).and_return(user)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).twice.and_return([])
          expect(ticket).to receive(:assignee).and_return(nil)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ labels: 'inprocess', assignee_id: 99 })

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end

        it 'updates an existing ticket and assignes it to the member if not member of project but group and puts it into inprocess' do
          m = MailStub.new('member@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(99)
          expect(gitlab).to receive(:user_search).with('member@example.com').and_return([user])
          allow_any_instance_of(Gitlab::Error::NotFound).to receive(:build_error_message).and_return('bla')
          expect(gitlab).to receive(:team_member).with(123,99).and_raise(Gitlab::Error::NotFound,'404 Not found')
          expect(gitlab).to receive(:group_member).with(91,99).and_return([user])
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).twice.and_return([])
          expect(ticket).to receive(:assignee).and_return(nil)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ labels: 'inprocess', assignee_id: 99 })

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end

        it 'updates an existing ticket and won\'t assign it if user is not team_member' do
          m = MailStub.new('member3@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(95)
          expect(gitlab).to receive(:user_search).with('member3@example.com').and_return([user])
          allow_any_instance_of(Gitlab::Error::NotFound).to receive(:build_error_message).and_return('bla')
          expect(gitlab).to receive(:team_member).with(123,95).and_raise(Gitlab::Error::NotFound,'404 Not found')
          expect(gitlab).to receive(:group_member).with(91,95).and_raise(Gitlab::Error::NotFound,'404 Not found')
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).and_return([])
          expect(ticket).not_to receive(:assignee)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member3@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).not_to receive(:edit_issue)

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end

        it 'updates an existing ticket and reassing it if already assigned' do
          m = MailStub.new('member2@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(99)
          user2 = double
          expect(user2).to receive(:id).and_return(97)
          expect(gitlab).to receive(:user_search).with('member2@example.com').and_return([user2])
          expect(gitlab).to receive(:team_member).with(123,97).and_return(user2)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).twice.and_return(['inprocess'])
          expect(ticket).to receive(:assignee).and_return(user)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member2@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ assignee_id: 97 })

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'updates a closed ticket but does not reopen it if subject still indicates ok' do
          m = MailStub.new('user@example.com','question [gl/tickets#42] [ok]')
          expect(gitlab).to receive(:user_search).with('user@example.com').and_return([])
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('closed')
          expect(ticket).to receive(:labels).and_return([])
          expect(ticket).not_to receive(:assignee)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42] [ok]")
          expect(gitlab).to receive(:edit_issue).never

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42] [ok]')
        end
        it 'reopens a closed ticket and puts it into process if subject does not contain ok' do
          m = MailStub.new('user@example.com','question [gl/tickets#42]')
          expect(gitlab).to receive(:user_search).with('user@example.com').and_return([])
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('closed')
          expect(ticket).to receive(:labels).twice.and_return([])
          expect(ticket).not_to receive(:assignee)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: user@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'reopen', labels: 'inprocess'})

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'reopens a closed ticket and reassignes it to the new member and puts it into process if subject does not contain ok' do
          m = MailStub.new('member2@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(99)
          user2 = double
          expect(user2).to receive(:id).and_return(97)
          expect(gitlab).to receive(:user_search).with('member2@example.com').and_return([user2])
          expect(gitlab).to receive(:team_member).with(123,97).and_return(user2)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('closed')
          expect(ticket).to receive(:labels).twice.and_return(['email'])
          expect(ticket).to receive(:assignee).and_return(user)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member2@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'reopen', labels: 'email,inprocess', assignee_id: 97})

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'reopens a closed ticket and assignes it to the member and puts it into process if subject does not contain ok' do
          m = MailStub.new('member@example.com','question [gl/tickets#42]')
          user = double
          expect(user).to receive(:id).and_return(99)
          expect(gitlab).to receive(:user_search).with('member@example.com').and_return([user])
          expect(gitlab).to receive(:team_member).with(123,99).and_return(user)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('closed')
          expect(ticket).to receive(:labels).twice.and_return([])
          expect(ticket).to receive(:assignee).and_return(nil)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'reopen', labels: 'inprocess', assignee_id: 99})

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42]')
        end
        it 'updates an existing ticket and assignes it to the member and closes it' do
          m = MailStub.new('member@example.com','question [gl/tickets#42] [ok]')
          user = double
          expect(user).to receive(:id).twice.and_return(99)
          expect(gitlab).to receive(:user_search).with('member@example.com').and_return([user])
          expect(gitlab).to receive(:team_member).with(123,99).and_return(user)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).and_return([])
          expect(ticket).to receive(:assignee).and_return(user)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42] [ok]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'close', labels: '' })

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42] [ok]')
        end
        it 'updates an existing ticket and assignes it to the member and closes it and removes the inprocess label' do
          m = MailStub.new('member@example.com','question [gl/tickets#42] [ok]')
          user = double
          expect(user).to receive(:id).twice.and_return(99)
          expect(gitlab).to receive(:user_search).with('member@example.com').and_return([user])
          expect(gitlab).to receive(:team_member).with(123,99).and_return(user)
          ticket = double
          expect(ticket).not_to receive(:iid)
          expect(ticket).to receive(:state).and_return('open')
          expect(ticket).to receive(:labels).twice.and_return(['email','inprocess','urgent'])
          expect(ticket).to receive(:assignee).and_return(user)
          expect(gitlab).to receive(:issue).with(123,42).and_return(ticket)
          expect(gitlab).to receive(:create_issue_note).with(123,42, "From: member@example.com\n\nMessage-Id: 1-abc-3\n\nSubject: question [gl/tickets#42] [ok]")
          expect(gitlab).to receive(:edit_issue).with(123,42,{ state_event: 'close', labels: 'email,urgent' })

          expect(list1.process(m)).to be(true)
          expect(m.subject).to eql('question [gl/tickets#42] [ok]')
        end
      end
    end
  end
end

