forked from Silverfish/proton-bridge
feat(GODT-2725): Implement receive message step with expected structure exposed.
This commit is contained in:
@ -21,9 +21,19 @@ Feature: IMAP import messages
|
|||||||
Hello
|
Hello
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to | subject | body |
|
"""
|
||||||
| bridgetest@pm.test | bridgetest@example.com | Basic text/plain message | Hello |
|
{
|
||||||
|
"from": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Internal Bridge <bridgetest@example.com>",
|
||||||
|
"subject": "Basic text/plain message",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"body-is": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
Scenario: Import message with double charset in content type
|
Scenario: Import message with double charset in content type
|
||||||
When IMAP client "1" appends the following message to "INBOX":
|
When IMAP client "1" appends the following message to "INBOX":
|
||||||
@ -39,9 +49,22 @@ Feature: IMAP import messages
|
|||||||
Hello
|
Hello
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to | subject | body |
|
"""
|
||||||
| bridgetest@pm.test | bridgetest@example.com | Message with double charset in content type | Hello |
|
{
|
||||||
|
"from": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Internal Bridge <bridgetest@example.com>",
|
||||||
|
"subject": "Message with double charset in content type",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"content-type-charset": "utf-8",
|
||||||
|
"content-disposition": "",
|
||||||
|
"transfer-encoding": "quoted-printable",
|
||||||
|
"body-is": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
Scenario: Import message with attachment name encoded by RFC 2047 without quoting
|
Scenario: Import message with attachment name encoded by RFC 2047 without quoting
|
||||||
@ -69,31 +92,87 @@ Feature: IMAP import messages
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
|
# And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
|
# """
|
||||||
|
# {
|
||||||
|
# "from": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
# "date": "01 Jan 80 00:00 +0000",
|
||||||
|
# "to": "Internal Bridge <bridgetest@protonmail.com>",
|
||||||
|
# "subject": "Message with attachment name encoded by RFC 2047 without quoting",
|
||||||
|
# "body-contains": "Hello",
|
||||||
|
# "content": {
|
||||||
|
# "content-type": "multipart/mixed; boundary=\"boundary\"",
|
||||||
|
# "sections":[
|
||||||
|
# {
|
||||||
|
# "content-type": "text/plain",
|
||||||
|
# "body-is": "Hello"
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "content-type": "application/pdf",
|
||||||
|
# "content-type-name": "=?US-ASCII?Q?filename?=",
|
||||||
|
# "content-disposition": "attachment",
|
||||||
|
# "content-disposition-filename": "=?US-ASCII?Q?filename?=",
|
||||||
|
# "body-is": "somebytes"
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# """
|
||||||
|
|
||||||
|
|
||||||
# The message is imported as UTF-8 and the content type is determined at build time.
|
# The message is imported as UTF-8 and the content type is determined at build time.
|
||||||
Scenario: Import message as latin1 without content type
|
Scenario: Import message as latin1 without content type
|
||||||
When IMAP client "1" appends "plain/text_plain_unknown_latin1.eml" to "INBOX"
|
When IMAP client "1" appends "plain/text_plain_unknown_latin1.eml" to "INBOX"
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to | body |
|
"""
|
||||||
| sender@pm.me | receiver@pm.me | ééééééé |
|
{
|
||||||
|
"from": "Sender <sender@pm.me>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Receiver <receiver@pm.me>",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"body-is": "ééééééé"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
# The message is imported and the body is converted to UTF-8.
|
# The message is imported and the body is converted to UTF-8.
|
||||||
Scenario: Import message as latin1 with content type
|
Scenario: Import message as latin1 with content type
|
||||||
When IMAP client "1" appends "plain/text_plain_latin1.eml" to "INBOX"
|
When IMAP client "1" appends "plain/text_plain_latin1.eml" to "INBOX"
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to | body |
|
"""
|
||||||
| sender@pm.me | receiver@pm.me | ééééééé |
|
{
|
||||||
|
"from": "Sender <sender@pm.me>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Receiver <receiver@pm.me>",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"content-type-charset": "utf-8",
|
||||||
|
"body-is": "ééééééé"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# The message is imported anad the body is wrongly converted (body is corrupted).
|
# The message is imported anad the body is wrongly converted (body is corrupted).
|
||||||
Scenario: Import message as latin1 with wrong content type
|
Scenario: Import message as latin1 with wrong content type
|
||||||
When IMAP client "1" appends "plain/text_plain_wrong_latin1.eml" to "INBOX"
|
When IMAP client "1" appends "plain/text_plain_wrong_latin1.eml" to "INBOX"
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to |
|
"""
|
||||||
| sender@pm.me | receiver@pm.me |
|
{
|
||||||
|
"from": "Sender <sender@pm.me>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Receiver <receiver@pm.me>",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"content-type-charset": "utf-8",
|
||||||
|
"body-is": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
Scenario: Import received message to Sent
|
Scenario: Import received message to Sent
|
||||||
When IMAP client "1" appends the following message to "Sent":
|
When IMAP client "1" appends the following message to "Sent":
|
||||||
@ -107,9 +186,19 @@ Feature: IMAP import messages
|
|||||||
Hello
|
Hello
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "Sent":
|
And IMAP client "1" eventually sees the following message in "Sent" with this structure:
|
||||||
| from | to | subject | body |
|
"""
|
||||||
| foo@example.com | bridgetest@pm.test | Hello | Hello |
|
{
|
||||||
|
"from": "Foo <foo@example.com>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
"subject": "Hello",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"body-is": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
And IMAP client "1" eventually sees 0 messages in "Inbox"
|
And IMAP client "1" eventually sees 0 messages in "Inbox"
|
||||||
|
|
||||||
Scenario: Import non-received message to Inbox
|
Scenario: Import non-received message to Inbox
|
||||||
@ -123,11 +212,22 @@ Feature: IMAP import messages
|
|||||||
Hello
|
Hello
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "INBOX":
|
And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
| from | to | subject | body |
|
"""
|
||||||
| foo@example.com | bridgetest@pm.test | Hello | Hello |
|
{
|
||||||
|
"from": "Foo <foo@example.com>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
"subject": "Hello",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"body-is": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
And IMAP client "1" eventually sees 0 messages in "Sent"
|
And IMAP client "1" eventually sees 0 messages in "Sent"
|
||||||
|
|
||||||
|
|
||||||
Scenario: Import non-received message to Sent
|
Scenario: Import non-received message to Sent
|
||||||
When IMAP client "1" appends the following message to "Sent":
|
When IMAP client "1" appends the following message to "Sent":
|
||||||
"""
|
"""
|
||||||
@ -139,10 +239,20 @@ Feature: IMAP import messages
|
|||||||
Hello
|
Hello
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "Sent":
|
|
||||||
| from | to | subject | body |
|
|
||||||
| foo@example.com | bridgetest@pm.test | Hello | Hello |
|
|
||||||
And IMAP client "1" eventually sees 0 messages in "Inbox"
|
And IMAP client "1" eventually sees 0 messages in "Inbox"
|
||||||
|
And IMAP client "1" eventually sees the following message in "Sent" with this structure:
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"from": "Foo <foo@example.com>",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
"subject": "Hello",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"body-is": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
Scenario Outline: Import message without sender to <mailbox>
|
Scenario Outline: Import message without sender to <mailbox>
|
||||||
When IMAP client "1" appends the following message to "<mailbox>":
|
When IMAP client "1" appends the following message to "<mailbox>":
|
||||||
@ -155,16 +265,53 @@ Feature: IMAP import messages
|
|||||||
Nope.
|
Nope.
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
And IMAP client "1" eventually sees the following messages in "<mailbox>":
|
And IMAP client "1" eventually sees the following message in "<mailbox>" with this structure:
|
||||||
| to | subject | body |
|
"""
|
||||||
| lionel@richie.com | RE: Hello, is it me you looking for? | Nope. |
|
{
|
||||||
|
"from": "Somebody@somewhere.org",
|
||||||
|
"date": "01 Jan 80 00:00 +0000",
|
||||||
|
"to": "Lionel Richie <lionel@richie.com>",
|
||||||
|
"subject": "RE: Hello, is it me you looking for?",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"content-type-charset":"utf-8",
|
||||||
|
"transfer-encoding":"quoted-printable",
|
||||||
|
"body-is": "Nope."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
Examples:
|
Examples:
|
||||||
| mailbox |
|
| mailbox |
|
||||||
| Drafts |
|
|
||||||
| Archive |
|
| Archive |
|
||||||
| Sent |
|
| Sent |
|
||||||
|
|
||||||
|
Scenario: Import message without sender to Drafts
|
||||||
|
When IMAP client "1" appends the following message to "Drafts":
|
||||||
|
"""
|
||||||
|
From: Somebody@somewhere.org
|
||||||
|
Date: 01 Jan 1980 00:00:00 +0000
|
||||||
|
To: Lionel Richie <lionel@richie.com>
|
||||||
|
Subject: RE: Hello, is it me you looking for?
|
||||||
|
|
||||||
|
Nope.
|
||||||
|
"""
|
||||||
|
Then it succeeds
|
||||||
|
And IMAP client "1" eventually sees the following message in "Drafts" with this structure:
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"date": "01 Jan 01 00:00 +0000",
|
||||||
|
"to": "Lionel Richie <lionel@richie.com>",
|
||||||
|
"subject": "RE: Hello, is it me you looking for?",
|
||||||
|
"content": {
|
||||||
|
"content-type": "text/plain",
|
||||||
|
"content-type-charset":"utf-8",
|
||||||
|
"transfer-encoding":"quoted-printable",
|
||||||
|
"body-is": "Nope."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
Scenario: Import embedded message
|
Scenario: Import embedded message
|
||||||
When IMAP client "1" appends the following message to "INBOX":
|
When IMAP client "1" appends the following message to "INBOX":
|
||||||
"""
|
"""
|
||||||
@ -198,3 +345,33 @@ Feature: IMAP import messages
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Then it succeeds
|
Then it succeeds
|
||||||
|
# And IMAP client "1" eventually sees the following message in "INBOX" with this structure:
|
||||||
|
# """
|
||||||
|
# {
|
||||||
|
# "from": "Foo <foo@example.com>",
|
||||||
|
# "date": "01 Jan 80 00:00 +0000",
|
||||||
|
# "to": "Bridge Test <bridgetest@pm.test>",
|
||||||
|
# "subject": "Embedded message",
|
||||||
|
# "body-contains": "Hello",
|
||||||
|
# "content": {
|
||||||
|
# "content-type": "multipart/mixed",
|
||||||
|
# "body-contains": "This is a multi-part message in MIME format.",
|
||||||
|
# "sections":[
|
||||||
|
# {
|
||||||
|
# "content-type": "text/plain",
|
||||||
|
# "content-type-charset": "utf-8",
|
||||||
|
# "transfer-encoding": "7bit",
|
||||||
|
# "body-is": ""
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "content-type": "message/rfc822",
|
||||||
|
# "content-type-name": "embedded.eml",
|
||||||
|
# "transfer-encoding": "7bit",
|
||||||
|
# "content-disposition": "attachment",
|
||||||
|
# "content-disposition-filename": "embedded.eml",
|
||||||
|
# "body-is": "From: Bar <bar@example.com>\n\rTo: Bridge Test <bridgetest@pm.test>\n\rSubject: (No Subject)\n\rContent-Type: text/plain; charset=utf-8\n\rContent-Transfer-Encoding: quoted-printable\n\r\n\rhello"
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# """
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package tests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -342,6 +343,26 @@ func (s *scenario) imapClientEventuallySeesTheFollowingMessagesInMailbox(clientI
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *scenario) imapClientSeesMessageInMailboxWithStructure(clientID, mailbox string, message *godog.DocString) error {
|
||||||
|
return eventually(func() error {
|
||||||
|
_, client := s.t.getIMAPClient(clientID)
|
||||||
|
|
||||||
|
var msgStruct MessageStruct
|
||||||
|
if err := json.Unmarshal([]byte(message.Content), &msgStruct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch, err := clientFetch(client, mailbox)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
haveMessages := xslices.Map(fetch, newMessageStructFromIMAP)
|
||||||
|
|
||||||
|
return matchStructure(haveMessages, msgStruct)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *scenario) imapClientSeesMessagesInMailbox(clientID string, count int, mailbox string) error {
|
func (s *scenario) imapClientSeesMessagesInMailbox(clientID string, count int, mailbox string) error {
|
||||||
_, client := s.t.getIMAPClient(clientID)
|
_, client := s.t.getIMAPClient(clientID)
|
||||||
|
|
||||||
|
|||||||
@ -143,6 +143,7 @@ func (s *scenario) steps(ctx *godog.ScenarioContext) {
|
|||||||
ctx.Step(`^IMAP client "([^"]*)" moves the message with subject "([^"]*)" from "([^"]*)" to "([^"]*)"$`, s.imapClientMovesTheMessageWithSubjectFromTo)
|
ctx.Step(`^IMAP client "([^"]*)" moves the message with subject "([^"]*)" from "([^"]*)" to "([^"]*)"$`, s.imapClientMovesTheMessageWithSubjectFromTo)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" moves all messages from "([^"]*)" to "([^"]*)"$`, s.imapClientMovesAllMessagesFromTo)
|
ctx.Step(`^IMAP client "([^"]*)" moves all messages from "([^"]*)" to "([^"]*)"$`, s.imapClientMovesAllMessagesFromTo)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following messages in "([^"]*)":$`, s.imapClientEventuallySeesTheFollowingMessagesInMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following messages in "([^"]*)":$`, s.imapClientEventuallySeesTheFollowingMessagesInMailbox)
|
||||||
|
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following message in "([^"]*)" with this structure:$`, s.imapClientSeesMessageInMailboxWithStructure)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" eventually sees (\d+) messages in "([^"]*)"$`, s.imapClientEventuallySeesMessagesInMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" eventually sees (\d+) messages in "([^"]*)"$`, s.imapClientEventuallySeesMessagesInMailbox)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" marks message (\d+) as deleted$`, s.imapClientMarksMessageAsDeleted)
|
ctx.Step(`^IMAP client "([^"]*)" marks message (\d+) as deleted$`, s.imapClientMarksMessageAsDeleted)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" marks the message with subject "([^"]*)" as deleted$`, s.imapClientMarksTheMessageWithSubjectAsDeleted)
|
ctx.Step(`^IMAP client "([^"]*)" marks the message with subject "([^"]*)" as deleted$`, s.imapClientMarksTheMessageWithSubjectAsDeleted)
|
||||||
|
|||||||
@ -56,6 +56,31 @@ type Message struct {
|
|||||||
References string `bdd:"references"`
|
References string `bdd:"references"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageStruct struct {
|
||||||
|
From string `json:"from"`
|
||||||
|
To string `json:"to"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
BCC string `json:"bcc"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
|
||||||
|
Content MessageSection `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageSection struct {
|
||||||
|
ContentType string `json:"content-type"`
|
||||||
|
ContentTypeBoundary string `json:"content-type-boundary"`
|
||||||
|
ContentTypeCharset string `json:"content-type-charset"`
|
||||||
|
ContentTypeName string `json:"content-type-name"`
|
||||||
|
ContentDisposition string `json:"content-disposition"`
|
||||||
|
ContentDispositionFilename string `json:"content-disposition-filename"`
|
||||||
|
Sections []MessageSection `json:"sections"`
|
||||||
|
|
||||||
|
TransferEncoding string `json:"transfer-encoding"`
|
||||||
|
BodyContains string `json:"body-contains"`
|
||||||
|
BodyIs string `json:"body-is"`
|
||||||
|
}
|
||||||
|
|
||||||
func (msg Message) Build() []byte {
|
func (msg Message) Build() []byte {
|
||||||
var b []byte
|
var b []byte
|
||||||
|
|
||||||
@ -166,6 +191,116 @@ func newMessageFromIMAP(msg *imap.Message) Message {
|
|||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMessageStructFromIMAP(msg *imap.Message) MessageStruct {
|
||||||
|
section, err := imap.ParseBodySectionName("BODY[]")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
literal, err := io.ReadAll(msg.GetBody(section))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := message.Parse(bytes.NewReader(literal))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var body string
|
||||||
|
if m.MIMEType == rfc822.TextPlain {
|
||||||
|
body = strings.TrimSpace(string(m.PlainBody))
|
||||||
|
} else {
|
||||||
|
body = strings.TrimSpace(string(m.RichBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := MessageStruct{
|
||||||
|
Subject: msg.Envelope.Subject,
|
||||||
|
Date: msg.Envelope.Date.Format(time.RFC822Z),
|
||||||
|
From: formatAddressList(msg.Envelope.From),
|
||||||
|
To: formatAddressList(msg.Envelope.To),
|
||||||
|
CC: formatAddressList(msg.Envelope.Cc),
|
||||||
|
BCC: formatAddressList(msg.Envelope.Bcc),
|
||||||
|
|
||||||
|
Content: parseMessageSection(literal, body),
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatAddressList(list []*imap.Address) string {
|
||||||
|
var res string
|
||||||
|
for idx, address := range list {
|
||||||
|
if address.PersonalName != "" {
|
||||||
|
res += address.PersonalName + " <" + address.Address() + ">"
|
||||||
|
} else {
|
||||||
|
res += address.Address()
|
||||||
|
}
|
||||||
|
if idx < len(list)-1 {
|
||||||
|
res += "; "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMessageSection(literal []byte, body string) MessageSection {
|
||||||
|
mimeType, boundary, charset, name := parseContentType(literal)
|
||||||
|
|
||||||
|
headers, err := rfc822.Parse(literal).ParseHeader()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgSect := MessageSection{
|
||||||
|
ContentType: string(mimeType),
|
||||||
|
ContentTypeBoundary: boundary,
|
||||||
|
ContentTypeCharset: charset,
|
||||||
|
ContentTypeName: name,
|
||||||
|
TransferEncoding: headers.Get("content-transfer-encoding"),
|
||||||
|
BodyIs: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
contentDisposition := bytes.Split([]byte(headers.Get("content-disposition")), []byte(";"))
|
||||||
|
for id, value := range contentDisposition {
|
||||||
|
if id == 0 {
|
||||||
|
msgSect.ContentDisposition = strings.TrimSpace(string(value))
|
||||||
|
}
|
||||||
|
param := bytes.Split(value, []byte("="))
|
||||||
|
if strings.TrimSpace(string(param[0])) == "filename" && len(param) >= 2 {
|
||||||
|
filename := strings.TrimPrefix(string(value), "filename=")
|
||||||
|
msgSect.ContentDispositionFilename = strings.TrimSpace(filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if msgSect.ContentTypeBoundary != "" {
|
||||||
|
sections := bytes.Split([]byte(msgSect.BodyIs), []byte("--"+msgSect.ContentTypeBoundary))
|
||||||
|
// Remove last element that will be the -- from finale boundary
|
||||||
|
sections = sections[:len(sections)-1]
|
||||||
|
for _, v := range sections {
|
||||||
|
msgSect.Sections = append(msgSect.Sections, parseMessageSection(v, string(v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msgSect
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseContentType(literal []byte) (rfc822.MIMEType, string, string, string) {
|
||||||
|
mimeType, params, err := rfc822.Parse(literal).ContentType()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
boundary, ok := params["boundary"]
|
||||||
|
if !ok {
|
||||||
|
boundary = ""
|
||||||
|
}
|
||||||
|
charset, ok := params["charset"]
|
||||||
|
if !ok {
|
||||||
|
charset = ""
|
||||||
|
}
|
||||||
|
name, ok := params["name"]
|
||||||
|
if !ok {
|
||||||
|
name = ""
|
||||||
|
}
|
||||||
|
return mimeType, boundary, charset, name
|
||||||
|
}
|
||||||
|
|
||||||
func matchMessages(have, want []Message) error {
|
func matchMessages(have, want []Message) error {
|
||||||
slices.SortFunc(have, func(a, b Message) bool {
|
slices.SortFunc(have, func(a, b Message) bool {
|
||||||
return a.Subject < b.Subject
|
return a.Subject < b.Subject
|
||||||
@ -182,6 +317,71 @@ func matchMessages(have, want []Message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func matchStructure(have []MessageStruct, want MessageStruct) error {
|
||||||
|
for _, msg := range have {
|
||||||
|
if want.From != "" && msg.From != want.From {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want.To != "" && msg.To != want.To {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want.BCC != "" && msg.BCC != want.BCC {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want.CC != "" && msg.CC != want.CC {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want.Subject != "" && msg.Subject != want.Subject {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want.Date != "" && want.Date != msg.Date {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchContent(msg.Content, want.Content) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("missing messages: have %#v, want %#v", have, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchContent(have MessageSection, want MessageSection) bool {
|
||||||
|
if want.ContentType != "" && want.ContentType != have.ContentType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.ContentTypeBoundary != "" && want.ContentTypeBoundary != have.ContentTypeBoundary {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.ContentTypeCharset != "" && want.ContentTypeCharset != have.ContentTypeCharset {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.ContentTypeName != "" && want.ContentTypeName != have.ContentTypeName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.ContentDisposition != "" && want.ContentDisposition != have.ContentDisposition {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.ContentDispositionFilename != "" && want.ContentDispositionFilename != have.ContentDispositionFilename {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.TransferEncoding != "" && want.TransferEncoding != have.TransferEncoding {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.BodyContains != "" && strings.Contains(have.BodyIs, want.BodyContains) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want.BodyIs != "" && want.BodyIs != have.BodyIs {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, section := range want.Sections {
|
||||||
|
if !matchContent(have, section) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type Mailbox struct {
|
type Mailbox struct {
|
||||||
Name string `bdd:"name"`
|
Name string `bdd:"name"`
|
||||||
Total int `bdd:"total"`
|
Total int `bdd:"total"`
|
||||||
@ -336,3 +536,7 @@ type Contact struct {
|
|||||||
Sign string `bdd:"signature"`
|
Sign string `bdd:"signature"`
|
||||||
Encrypt string `bdd:"encryption"`
|
Encrypt string `bdd:"encryption"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FullAddress(addr *imap.Address) string {
|
||||||
|
return addr.PersonalName + " <" + addr.MailboxName + "@" + addr.HostName + ">"
|
||||||
|
}
|
||||||
|
|||||||
@ -558,7 +558,7 @@ func (s *scenario) createUserAccount(username, password string, disabled bool) e
|
|||||||
if _, err := s.t.runQuarkCmd(
|
if _, err := s.t.runQuarkCmd(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
"user:create:subscription",
|
"user:create:subscription",
|
||||||
"--planID", "plus",
|
"--planID", "visionary2022",
|
||||||
string(userDecID),
|
string(userDecID),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user