feat(GODT-1264): constraint on Scheduled mailbox in connector + Integration tests.

This commit is contained in:
Xavier Michelon
2023-02-15 07:37:09 +00:00
parent 13db1b0db8
commit 08dab2d115
8 changed files with 74 additions and 66 deletions

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/Masterminds/semver/v3 v3.1.1 github.com/Masterminds/semver/v3 v3.1.1
github.com/ProtonMail/gluon v0.14.2-0.20230207142445-9f98ae47a031 github.com/ProtonMail/gluon v0.14.2-0.20230207142445-9f98ae47a031
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/ProtonMail/go-proton-api v0.4.0 github.com/ProtonMail/go-proton-api v0.4.1-0.20230214130336-4056d48a12e1
github.com/ProtonMail/go-rfc5322 v0.11.0 github.com/ProtonMail/go-rfc5322 v0.11.0
github.com/ProtonMail/gopenpgp/v2 v2.4.10 github.com/ProtonMail/gopenpgp/v2 v2.4.10
github.com/PuerkitoBio/goquery v1.8.0 github.com/PuerkitoBio/goquery v1.8.0

6
go.sum
View File

@ -41,8 +41,10 @@ github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NB
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc= github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc=
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM=
github.com/ProtonMail/go-proton-api v0.4.0 h1:Tw8Ieuc355ljPqpIzh/uttpE+5ia0z8GA/ca5iyl/9w= github.com/ProtonMail/go-proton-api v0.4.1-0.20230214112101-944a29edf66a h1:/31/wWgxpBWREaRGRMVH4AVgQGpPtTK9BsLjt1a8uwc=
github.com/ProtonMail/go-proton-api v0.4.0/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos= github.com/ProtonMail/go-proton-api v0.4.1-0.20230214112101-944a29edf66a/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos=
github.com/ProtonMail/go-proton-api v0.4.1-0.20230214130336-4056d48a12e1 h1:AjdiuiUwDz9ADIzEccii+D91YYz0w0doGXZs12j4XcY=
github.com/ProtonMail/go-proton-api v0.4.1-0.20230214130336-4056d48a12e1/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos=
github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY= github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY=
github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw= github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw=
github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg=

View File

@ -24,7 +24,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -44,8 +43,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const scheduled = "Scheduled"
func TestBridge_Sync(t *testing.T) { func TestBridge_Sync(t *testing.T) {
numMsg := 1 << 8 numMsg := 1 << 8
@ -471,45 +468,3 @@ func countBytesRead(ctl *proton.NetCtl, fn func()) uint64 {
return read return read
} }
func TestBridge_ScheduledLabel(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
hasScheduledSystemLabel := func(imapClient *client.Client) bool {
return xslices.Any(clientList(imapClient), func(mailboxInfo *imap.MailboxInfo) bool { return mailboxInfo.Name == scheduled })
}
_, _, err := s.CreateUser(username, password)
require.NoError(t, err)
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(b *bridge.Bridge, _ *bridge.Mocks) {
// Perform initial sync
syncCh, done := chToType[events.Event, events.SyncFinished](b.GetEvents(events.SyncFinished{}))
defer done()
userID, err := b.LoginFull(ctx, username, password, nil, nil)
require.NoError(t, err)
require.Equal(t, userID, (<-syncCh).UserID)
// connect an IMAP client
info, err := b.GetUserInfo(userID)
require.NoError(t, err)
imapClient, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, b.GetIMAPPort()))
require.NoError(t, err)
require.NoError(t, imapClient.Login(info.Addresses[0], string(info.BridgePass)))
defer func() { _ = imapClient.Logout() }()
// Scheduled mailbox is empty. It's not listed.
require.False(t, hasScheduledSystemLabel(imapClient))
// Add a message to the Schedule mailbox. It's now listed.
require.NoError(t, imapClient.Append(scheduled, []string{}, time.Now(), strings.NewReader("To: no_reply@pm.me")))
require.True(t, hasScheduledSystemLabel(imapClient))
// delete message from Scheduled. The mailbox is now empty and not listed anymore
_, err = imapClient.Select(scheduled, false)
require.NoError(t, err)
require.NoError(t, clientStore(imapClient, 1, 1, true, imap.FormatFlagsOp(imap.AddFlags, true), imap.DeletedFlag))
require.NoError(t, imapClient.Expunge(nil))
require.False(t, hasScheduledSystemLabel(imapClient))
})
})
}

View File

@ -380,7 +380,7 @@ func (conn *imapConnector) GetMessageLiteral(ctx context.Context, id imap.Messag
func (conn *imapConnector) AddMessagesToMailbox(ctx context.Context, messageIDs []imap.MessageID, mailboxID imap.MailboxID) error { func (conn *imapConnector) AddMessagesToMailbox(ctx context.Context, messageIDs []imap.MessageID, mailboxID imap.MailboxID) error {
defer conn.goPollAPIEvents(false) defer conn.goPollAPIEvents(false)
if mailboxID == proton.AllMailLabel { if isAllMailOrScheduled(mailboxID) {
return fmt.Errorf("not allowed") return fmt.Errorf("not allowed")
} }
@ -391,7 +391,7 @@ func (conn *imapConnector) AddMessagesToMailbox(ctx context.Context, messageIDs
func (conn *imapConnector) RemoveMessagesFromMailbox(ctx context.Context, messageIDs []imap.MessageID, mailboxID imap.MailboxID) error { func (conn *imapConnector) RemoveMessagesFromMailbox(ctx context.Context, messageIDs []imap.MessageID, mailboxID imap.MailboxID) error {
defer conn.goPollAPIEvents(false) defer conn.goPollAPIEvents(false)
if mailboxID == proton.AllMailLabel { if isAllMailOrScheduled(mailboxID) {
return fmt.Errorf("not allowed") return fmt.Errorf("not allowed")
} }
@ -440,8 +440,8 @@ func (conn *imapConnector) MoveMessages(ctx context.Context, messageIDs []imap.M
if (labelFromID == proton.InboxLabel && labelToID == proton.SentLabel) || if (labelFromID == proton.InboxLabel && labelToID == proton.SentLabel) ||
(labelFromID == proton.SentLabel && labelToID == proton.InboxLabel) || (labelFromID == proton.SentLabel && labelToID == proton.InboxLabel) ||
labelFromID == proton.AllMailLabel || isAllMailOrScheduled(labelFromID) ||
labelToID == proton.AllMailLabel { isAllMailOrScheduled(labelToID) {
return false, fmt.Errorf("not allowed") return false, fmt.Errorf("not allowed")
} }
@ -691,3 +691,7 @@ func toIMAPMailbox(label proton.Label, flags, permFlags, attrs imap.FlagSet) ima
Attributes: attrs, Attributes: attrs,
} }
} }
func isAllMailOrScheduled(mailboxID imap.MailboxID) bool {
return (mailboxID == proton.AllMailLabel) || (mailboxID == proton.AllScheduledLabel)
}

View File

@ -36,4 +36,27 @@ Feature: IMAP list mailboxes
Then IMAP client "1" counts 20 mailboxes under "Folders" Then IMAP client "1" counts 20 mailboxes under "Folders"
And IMAP client "1" counts 60 mailboxes under "Labels" And IMAP client "1" counts 60 mailboxes under "Labels"
Then IMAP client "2" counts 20 mailboxes under "Folders" Then IMAP client "2" counts 20 mailboxes under "Folders"
And IMAP client "2" counts 60 mailboxes under "Labels" And IMAP client "2" counts 60 mailboxes under "Labels"
Scenario: List with scheduled mail
Given there exists an account with username "[user:user]" and password "password"
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Scheduled":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | sch | false |
When bridge starts
And the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing
And user "[user:user]" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following mailbox info:
| name | total |
| INBOX | 0 |
| Drafts | 0 |
| Sent | 0 |
| Starred | 0 |
| Archive | 0 |
| Spam | 0 |
| Trash | 0 |
| All Mail | 1 |
| Folders | 0 |
| Labels | 0 |
| Scheduled | 1 |

View File

@ -6,6 +6,7 @@ Feature: IMAP remove messages from mailbox
| mbox | folder | | mbox | folder |
| label | label | | label | label |
And the address "[user:user]@[domain]" of account "[user:user]" has 10 messages in "Folders/mbox" And the address "[user:user]@[domain]" of account "[user:user]" has 10 messages in "Folders/mbox"
And the address "[user:user]@[domain]" of account "[user:user]" has 1 messages in "Scheduled"
And bridge starts And bridge starts
And the user logs in with username "[user:user]" and password "password" And the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing And user "[user:user]" finishes syncing
@ -44,4 +45,11 @@ Feature: IMAP remove messages from mailbox
And IMAP client "1" marks message 2 as deleted And IMAP client "1" marks message 2 as deleted
And it succeeds And it succeeds
And IMAP client "1" expunges And IMAP client "1" expunges
Then it fails Then it fails
Scenario: Not possible to delete from Scheduled and expunge does nothing
When IMAP client "1" selects "Scheduled"
And IMAP client "1" marks message 1 as deleted
Then it succeeds
And IMAP client "1" expunges
Then it fails

View File

@ -16,6 +16,9 @@ Feature: IMAP move messages
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Sent": And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Sent":
| from | to | subject | unread | | from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | bax | false | | john.doe@mail.com | [user:user]@[domain] | bax | false |
And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Scheduled":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | sch | false |
And bridge starts And bridge starts
And the user logs in with username "[user:user]" and password "password" And the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing And user "[user:user]" finishes syncing
@ -96,8 +99,17 @@ Feature: IMAP move messages
| jane.doe@mail.com | name@[domain] | bar | true | | jane.doe@mail.com | name@[domain] | bar | true |
| john.doe@mail.com | [user:user]@[domain] | baz | false | | john.doe@mail.com | [user:user]@[domain] | baz | false |
| john.doe@mail.com | [user:user]@[domain] | bax | false | | john.doe@mail.com | [user:user]@[domain] | bax | false |
| john.doe@mail.com | [user:user]@[domain] | sch | false |
Scenario: Move message from Inbox to Sent is not possible Scenario: Move message from Scheduled is not possible
Given test skips reporter checks
When IMAP client "1" moves the message with subject "sch" from "Scheduled" to "Inbox"
Then it fails
And IMAP client "1" eventually sees the following messages in "Scheduled":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | sch | false |
Scenario: Move message from Inbox to Sent is not possible
Given test skips reporter checks Given test skips reporter checks
When IMAP client "1" moves the message with subject "bar" from "Inbox" to "Sent" When IMAP client "1" moves the message with subject "bar" from "Inbox" to "Sent"
Then it fails Then it fails
@ -105,4 +117,4 @@ Feature: IMAP move messages
Scenario: Move message from Sent to Inbox is not possible Scenario: Move message from Sent to Inbox is not possible
Given test skips reporter checks Given test skips reporter checks
When IMAP client "1" moves the message with subject "bax" from "Sent" to "Inbox" When IMAP client "1" moves the message with subject "bax" from "Sent" to "Inbox"
Then it fails Then it fails

View File

@ -193,14 +193,6 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
return err return err
} }
var messageFlags proton.MessageFlag
if !strings.EqualFold(mailbox, "Sent") {
messageFlags = proton.MessageFlagReceived
} else {
messageFlags = proton.MessageFlagSent
}
return s.t.createMessages(ctx, username, addrID, xslices.Map(wantMessages, func(message Message) proton.ImportReq { return s.t.createMessages(ctx, username, addrID, xslices.Map(wantMessages, func(message Message) proton.ImportReq {
return proton.ImportReq{ return proton.ImportReq{
@ -208,7 +200,7 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
AddressID: addrID, AddressID: addrID,
LabelIDs: []string{mboxID}, LabelIDs: []string{mboxID},
Unread: proton.Bool(message.Unread), Unread: proton.Bool(message.Unread),
Flags: messageFlags, Flags: flagsForMailbox(mailbox),
}, },
Message: message.Build(), Message: message.Build(),
} }
@ -228,7 +220,7 @@ func (s *scenario) theAddressOfAccountHasMessagesInMailbox(address, username str
Metadata: proton.ImportMetadata{ Metadata: proton.ImportMetadata{
AddressID: addrID, AddressID: addrID,
LabelIDs: []string{mboxID}, LabelIDs: []string{mboxID},
Flags: proton.MessageFlagReceived, Flags: flagsForMailbox(mailbox),
}, },
Message: Message{ Message: Message{
Subject: fmt.Sprintf("%d", idx), Subject: fmt.Sprintf("%d", idx),
@ -240,6 +232,18 @@ func (s *scenario) theAddressOfAccountHasMessagesInMailbox(address, username str
}))) })))
} }
func flagsForMailbox(mailboxName string) proton.MessageFlag {
if strings.EqualFold(mailboxName, "Sent") {
return proton.MessageFlagSent
}
if strings.EqualFold(mailboxName, "Scheduled") {
return proton.MessageFlagScheduledSend
}
return proton.MessageFlagReceived
}
// accountDraftChanged changes the draft attributes, where draftIndex is // accountDraftChanged changes the draft attributes, where draftIndex is
// similar to sequential ID i.e. 1 represents the first message of draft folder // similar to sequential ID i.e. 1 represents the first message of draft folder
// sorted by API creation time. // sorted by API creation time.