GODT-966 Append internal message to AllMail should be no action

GODT-966 Fixed CI issues (WIP)

WIP GODT-966 modified behavior of APPEND.

WIP GODT-966 implemented test for possible order of operations.

WIP GODT-966: code cleanup and refactoring.

WIP GODT-966 fix for linter.

Other: Minor refactoring.
This commit is contained in:
Xavier Michelon
2021-09-16 15:37:55 +02:00
committed by Jakub Cuth
parent c35ff4f913
commit 63f089540e
4 changed files with 111 additions and 11 deletions

View File

@ -116,11 +116,10 @@ func (im *imapMailbox) createMessage(imapFlags []string, date time.Time, r imap.
if internalID != "" {
if msg, err := im.storeMailbox.GetMessage(internalID); err == nil {
if im.user.user.IsCombinedAddressMode() || im.storeAddress.AddressID() == msg.Message().AddressID {
return im.labelExistingMessage(msg.ID(), msg.IsMarkedDeleted())
return im.labelExistingMessage(msg)
}
}
}
return im.importMessage(kr, hdr, body, imapFlags, date)
}
@ -146,7 +145,7 @@ func (im *imapMailbox) createDraftMessage(kr *crypto.KeyRing, email string, body
return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{draft.ID}))
}
func (im *imapMailbox) labelExistingMessage(messageID string, isDeleted bool) error {
func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { //nolint[funlen]
im.log.Info("Labelling existing message")
// IMAP clients can move message to local folder (setting \Deleted flag)
@ -156,17 +155,37 @@ func (im *imapMailbox) labelExistingMessage(messageID string, isDeleted bool) er
// not delete the message (EXPUNGE would delete the original message and
// the new duplicate one would stay). API detects duplicates; therefore
// we need to remove \Deleted flag if IMAP client re-imports.
if isDeleted {
if err := im.storeMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil {
if msg.IsMarkedDeleted() {
if err := im.storeMailbox.MarkMessagesUndeleted([]string{msg.ID()}); err != nil {
log.WithError(err).Error("Failed to undelete re-imported message")
}
}
if err := im.storeMailbox.LabelMessages([]string{messageID}); err != nil {
storeMBox := im.storeMailbox
// Outlook Uses APPEND instead of COPY. There is no need to copy to All Mail because messages are already there.
// if the message is copied from Spam or Trash, it should stay as is. Return error.
// Otherwise the message is already in All mail, Return OK.
if pmapi.AllMailLabel == storeMBox.LabelID() {
foundArchive := false
for _, mBox := range im.storeAddress.ListMailboxes() {
if mBox.LabelID() == pmapi.ArchiveLabel {
foundArchive = true
storeMBox = mBox
break
}
}
if !foundArchive {
return errors.New("could not find Archive folder")
}
}
if err := storeMBox.LabelMessages([]string{msg.ID()}); err != nil {
return err
}
return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{messageID}))
return uidplus.AppendResponse(storeMBox.UIDValidity(), storeMBox.GetUIDList([]string{msg.ID()}))
}
func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error {

View File

@ -12,7 +12,7 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
| 2 | jane.doe@mail.com | name@pm.me | bar | world |
And there is IMAP client "source" selected in "INBOX"
And there is IMAP client "target" selected in "<mailbox>"
When IMAP clients "source" and "target" move message seq "2" of "user" from "INBOX" to "<mailbox>" by append and delete
When IMAP clients "source" and "target" move message seq "2" of "user" from "INBOX" to "<mailbox>"
Then IMAP response to "source" is "OK"
Then IMAP response to "target" is "OK"
When IMAP client "source" sends expunge
@ -38,7 +38,7 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
| 2 | jane.doe@mail.com | name@pm.me | bar | world |
And there is IMAP client "source" selected in "<mailbox>"
And there is IMAP client "target" selected in "INBOX"
When IMAP clients "source" and "target" move message seq "2" of "user" from "<mailbox>" to "INBOX" by append and delete
When IMAP clients "source" and "target" move message seq "2" of "user" from "<mailbox>" to "INBOX"
Then IMAP response to "source" is "OK"
Then IMAP response to "target" is "OK"
When IMAP client "source" sends expunge
@ -54,3 +54,35 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
| mailbox |
| Spam |
| Trash |
Scenario Outline: Move message from <mailbox> to All Mail by <order>
Given there are messages in mailbox "<mailbox>" for "user"
| id | from | to | subject | body |
| 1 | john.doe@mail.com | user@pm.me | subj1 | body1 |
| 2 | john.doe@mail.com | name@pm.me | subj2 | body2 |
And there is IMAP client "source" selected in "<mailbox>"
And there is IMAP client "target" selected in "All Mail"
When IMAP clients "source" and "target" move message seq "2" of "user" to "All Mail" by <order>
Then IMAP response to "source" is "OK"
Then IMAP response to "target" is "OK"
And mailbox "<mailbox>" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | subj1 |
And mailbox "All Mail" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | subj1 |
| john.doe@mail.com | name@pm.me | subj2 |
Examples:
| mailbox | order |
| INBOX | APPEND DELETE EXPUNGE |
| Archive | APPEND DELETE EXPUNGE |
| Trash | APPEND DELETE EXPUNGE |
| Spam | APPEND DELETE EXPUNGE |
| INBOX | DELETE APPEND EXPUNGE |
| Archive | DELETE APPEND EXPUNGE |
| Trash | DELETE APPEND EXPUNGE |
| Spam | DELETE APPEND EXPUNGE |
| INBOX | DELETE EXPUNGE APPEND |
| Archive | DELETE EXPUNGE APPEND |
| Trash | DELETE EXPUNGE APPEND |
| Spam | DELETE EXPUNGE APPEND |

View File

@ -18,10 +18,14 @@
package tests
import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/ProtonMail/proton-bridge/test/mocks"
"github.com/cucumber/godog"
"golang.org/x/net/html/charset"
)
@ -35,7 +39,8 @@ func IMAPActionsMessagesFeatureContext(s *godog.ScenarioContext) {
s.Step(`^IMAP client searches for "([^"]*)"$`, imapClientSearchesFor)
s.Step(`^IMAP client copies message seq "([^"]*)" to "([^"]*)"$`, imapClientCopiesMessagesTo)
s.Step(`^IMAP client moves message seq "([^"]*)" to "([^"]*)"$`, imapClientMovesMessagesTo)
s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" from "([^"]*)" to "([^"]*)" by append and delete$`, imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete)
s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" from "([^"]*)" to "([^"]*)"$`, imapClientsMoveMessageSeqOfUserFromTo)
s.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message seq "([^"]*)" of "([^"]*)" to "([^"]*)" by ([^"]*) ([^"]*) ([^"]*)`, imapClientsMoveMessageSeqOfUserFromToByOrederedOperations)
s.Step(`^IMAP client imports message to "([^"]*)"$`, imapClientCreatesMessage)
s.Step(`^IMAP client imports message to "([^"]*)" with encoding "([^"]*)"$`, imapClientCreatesMessageWithEncoding)
s.Step(`^IMAP client creates message "([^"]*)" from "([^"]*)" to "([^"]*)" with body "([^"]*)" in "([^"]*)"$`, imapClientCreatesMessageFromToWithBody)
@ -113,7 +118,7 @@ func imapClientMovesMessagesTo(messageSeq, newMailboxName string) error {
return nil
}
func imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, sourceMailboxName, targetMailboxName string) error {
func imapClientsMoveMessageSeqOfUserFromTo(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, sourceMailboxName, targetMailboxName string) error {
account := ctx.GetTestAccount(bddUserID)
if account == nil {
return godog.ErrPending
@ -160,6 +165,45 @@ func imapClientsMoveMessageSeqOfUserFromToByAppendAndDelete(sourceIMAPClient, ta
return nil
}
func extractMessageBodyFromImapResponse(response *mocks.IMAPResponse) (string, error) {
sections := response.Sections()
if len(sections) != 1 {
return "", internalError(errors.New("unexpected result from FETCH"), "retrieving message body using FETCH")
}
sections = strings.Split(sections[0], "\n")
if len(sections) < 2 {
return "", internalError(errors.New("failed to parse FETCH result"), "extraction body from FETCH reply")
}
return strings.Join(sections[1:], "\n"), nil
}
func imapClientsMoveMessageSeqOfUserFromToByOrederedOperations(sourceIMAPClient, targetIMAPClient, messageSeq, bddUserID, targetMailboxName, op1, op2, op3 string) error {
account := ctx.GetTestAccount(bddUserID)
if account == nil {
return godog.ErrPending
}
msgStr, err := extractMessageBodyFromImapResponse(ctx.GetIMAPClient(sourceIMAPClient).Fetch(messageSeq, "BODY.PEEK[]").AssertOK())
if err != nil {
return err
}
for _, op := range []string{op1, op2, op3} {
switch op {
case "APPEND":
res := ctx.GetIMAPClient(targetIMAPClient).Append(targetMailboxName, msgStr)
ctx.SetIMAPLastResponse(targetIMAPClient, res)
case "DELETE":
_ = imapClientNamedMarksMessageSeqAsDeleted(sourceIMAPClient, messageSeq)
case "EXPUNGE":
_ = imapClientNamedExpunge(sourceIMAPClient)
default:
return errors.New("unknow IMAP operation " + op)
}
time.Sleep(100 * time.Millisecond)
}
return nil
}
func imapClientCreatesMessage(mailboxName string, message *godog.DocString) error {
return imapClientCreatesMessageWithEncoding(mailboxName, "utf8", message)
}

View File

@ -107,6 +107,11 @@ func (ir *IMAPResponse) AssertOK() *IMAPResponse {
return ir
}
func (ir *IMAPResponse) Sections() []string {
ir.wait()
return ir.sections
}
func (ir *IMAPResponse) AssertResult(wantResult string) *IMAPResponse {
ir.wait()
a.NoError(ir.t, ir.err)