discourse/spec/lib/imap/providers/generic_spec.rb
Martin Brennan 95b71b35d6
FEATURE: IMAP delete email sync for group inboxes (#10392)
Adds functionality to reflect topic delete in Discourse to IMAP inbox (Gmail only for now) and reflecting Gmail deletes in Discourse.

Adding lots of tests, various refactors and code improvements.

When Discourse topic is destroyed in PostDestroyer mark the topic incoming email as imap_sync: true, and do the opposite when post is recovered.
2020-08-12 10:16:26 +10:00

216 lines
6.2 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Imap::Providers::Generic do
fab!(:username) { "test@generic.com" }
fab!(:password) { "test1!" }
fab!(:provider) do
described_class.new(
"imap.generic.com",
{
port: 993,
ssl: true,
username: username,
password: password
}
)
end
let(:dummy_mailboxes) do
[
Net::IMAP::MailboxList.new([], "/", "All Mail"),
Net::IMAP::MailboxList.new([:Noselect], "/", "Other"),
Net::IMAP::MailboxList.new([:Trash], "/", "Bin")
]
end
let(:imap_stub) { stub }
before do
described_class.any_instance.stubs(:imap).returns(imap_stub)
end
describe "#connect!" do
it "calls login with the provided username and password on the imap client" do
imap_stub.expects(:login).with(username, password).once
provider.connect!
end
end
describe "#list_mailboxes" do
before do
imap_stub.expects(:list).with('', '*').returns(dummy_mailboxes)
end
it "does not return any mailboxes with the Noselect attribute" do
expect(provider.list_mailboxes).not_to include("Other")
end
it "filters by the provided attribute" do
expect(provider.list_mailboxes(:Trash)).to eq(["Bin"])
end
it "lists all mailboxes names" do
expect(provider.list_mailboxes).to eq(["All Mail", "Bin"])
end
end
describe "#trash_mailbox" do
before do
imap_stub.expects(:list).with('', '*').returns(dummy_mailboxes)
Discourse.cache.delete("imap_trash_mailbox_#{provider.account_digest}")
end
it "returns the mailbox with the special-use attribute \Trash" do
expect(provider.trash_mailbox).to eq("Bin")
end
it "caches the result based on the account username and server for 30 mins" do
provider.trash_mailbox
provider.expects(:list_mailboxes).never
provider.trash_mailbox
end
end
describe "#find_trashed_by_message_ids" do
before do
provider.stubs(:trash_mailbox).returns("Bin")
imap_stub.stubs(:examine).with("Inbox").twice
imap_stub.stubs(:responses).returns({ 'UIDVALIDITY' => [1] })
imap_stub.stubs(:examine).with("Bin")
imap_stub.stubs(:responses).returns({ 'UIDVALIDITY' => [9] })
provider.expects(:emails).with([4, 6], ['UID', 'ENVELOPE']).returns(
[
{
'ENVELOPE' => stub(message_id: "<h4786x34@test.com>"),
'UID' => 4
},
{
'ENVELOPE' => stub(message_id: "<f349xj84@test.com>"),
'UID' => 6
}
]
)
end
let(:message_ids) do
[
"h4786x34@test.com",
"dvsfuf39@test.com",
"f349xj84@test.com"
]
end
it "sends the message-id search in the correct format and returns the trashed emails and UIDVALIDITY" do
provider.open_mailbox("Inbox")
imap_stub.expects(:uid_search).with(
"OR OR HEADER Message-ID '<h4786x34@test.com>' HEADER Message-ID '<dvsfuf39@test.com>' HEADER Message-ID '<f349xj84@test.com>'"
).returns([4, 6])
resp = provider.find_trashed_by_message_ids(message_ids)
expect(resp.trashed_emails.map(&:message_id)).to match_array(['h4786x34@test.com', 'f349xj84@test.com'])
expect(resp.trash_uid_validity).to eq(9)
end
end
describe "#trash" do
it "stores the \Deleted flag on the UID and expunges" do
provider.stubs(:can?).with('MOVE').returns(false)
provider.expects(:store).with(78, 'FLAGS', [], ['\Deleted'])
imap_stub.expects(:expunge)
provider.trash(78)
end
context "if the server supports MOVE" do
it "calls trash_move which is implemented by the provider" do
provider.stubs(:can?).with('MOVE').returns(true)
provider.expects(:trash_move).with(78)
provider.trash(78)
end
end
end
describe "#uids" do
it "can search with from and to" do
imap_stub.expects(:uid_search).once.with("UID 5:9")
provider.uids(from: 5, to: 9)
end
it "can search with only from" do
imap_stub.expects(:uid_search).once.with("UID 5:*")
provider.uids(from: 5)
end
it "can search with only to" do
imap_stub.expects(:uid_search).once.with("UID 1:9")
provider.uids(to: 9)
end
it "can search all" do
imap_stub.expects(:uid_search).once.with("ALL")
provider.uids
end
end
describe "#open_mailbox" do
it "uses examine to get a readonly version of the mailbox" do
imap_stub.expects(:examine).with("Inbox")
imap_stub.expects(:responses).returns({ 'UIDVALIDITY' => [1] })
provider.open_mailbox("Inbox")
end
describe "write true" do
context "if imap_write is disabled" do
before { SiteSetting.enable_imap_write = false }
it "raises an error" do
expect { provider.open_mailbox("Inbox", write: true) }.to raise_error(
Imap::Providers::WriteDisabledError
)
end
end
context "if imap_write is enabled" do
before { SiteSetting.enable_imap_write = true }
it "does not raise an error and calls imap.select" do
imap_stub.expects(:select).with("Inbox")
imap_stub.expects(:responses).returns({ 'UIDVALIDITY' => [1] })
expect { provider.open_mailbox("Inbox", write: true) }.not_to raise_error
end
end
end
end
describe "#emails" do
let(:fields) { ['UID'] }
let(:uids) { [99, 106] }
it "returns empty array if uid_fetch does not find any matching emails by uid" do
imap_stub.expects(:uid_fetch).with(uids, fields).returns(nil)
expect(provider.emails(uids, fields)).to eq([])
end
it "returns an array of attributes" do
imap_stub.expects(:uid_fetch).with(uids, fields).returns([
Net::IMAP::FetchData.new(1, { "UID" => 99 }),
Net::IMAP::FetchData.new(1, { "UID" => 106 })
])
expect(provider.emails(uids, fields)).to eq([{ "UID" => 99 }, { "UID" => 106 }])
end
end
describe "#to_tag" do
it "returns a label cleaned up so it can be used for a discourse tag" do
expect(provider.to_tag("Some Label")).to eq("some-label")
end
end
describe "#tag_to_label" do
it "returns the tag as is by default" do
expect(provider.tag_to_label("Support")).to eq("Support")
end
end
end