GODT-1650: text/html sending tests

This commit is contained in:
James Houlahan
2022-10-04 16:27:38 +02:00
parent ba9368426c
commit c953b8030a
12 changed files with 535 additions and 165 deletions

View File

@ -1,11 +1,27 @@
package tests
import (
"encoding/json"
"reflect"
"github.com/bradenaw/juniper/xslices"
)
func ToAny(v any) any {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
var a any
if err := json.Unmarshal(b, &a); err != nil {
panic(err)
}
return a
}
func IsSub(outer, inner any) bool {
if outer == nil && inner != nil {
return IsSub(reflect.Zero(reflect.TypeOf(inner)).Interface(), inner)
@ -30,10 +46,6 @@ func IsSub(outer, inner any) bool {
return false
}
if len(inner) != len(outer) {
return false
}
return isSubSlice(outer, inner)
default:
@ -69,6 +81,10 @@ func isSubMap(outer, inner map[string]any) bool {
}
func isSubSlice(outer, inner []any) bool {
if len(inner) != len(outer) {
return false
}
for _, v := range inner {
if xslices.IndexFunc(outer, func(outer any) bool {
return IsSub(outer, v)

View File

@ -5,7 +5,7 @@ Feature: IMAP get mailbox info
| name | type |
| one | folder |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And bridge starts

View File

@ -6,7 +6,7 @@ Feature: IMAP copy messages
| mbox | folder |
| label | label |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "Inbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And bridge starts
@ -17,37 +17,37 @@ Feature: IMAP copy messages
Scenario: Copy message to label
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Labels/label"
Then IMAP client "1" sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
Scenario: Copy all messages to label
When IMAP client "1" copies all messages from "INBOX" to "Labels/label"
Then IMAP client "1" sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
Scenario: Copy message to folder does move
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Folders/mbox"
Then IMAP client "1" eventually sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Folders/mbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
Scenario: Copy all messages to folder does move
When IMAP client "1" copies all messages from "INBOX" to "Folders/mbox"
Then IMAP client "1" sees the following messages in "Folders/mbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" eventually sees 0 messages in "INBOX"
@ -55,7 +55,7 @@ Feature: IMAP copy messages
Scenario: Copy message from Inbox to Sent is not possible
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Sent"
Then IMAP client "1" eventually sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" eventually sees 0 messages in "Sent"

View File

@ -0,0 +1,344 @@
Feature: SMTP sending of plain messages
Background:
Given there exists an account with username "user@pm.me" and password "password"
And there exists an account with username "bridgetest@protonmail.com" and password "password"
And there exists an account with username "bridgetest2@protonmail.com" and password "password"
And bridge starts
And the user logs in with username "user@pm.me" and password "password"
And user "user@pm.me" connects and authenticates SMTP client "1"
Scenario: HTML message to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: HTML text external
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=utf-8
In-Reply-To: <base64hashOfSomeMessage@protonmail.internalid>
<html><body>This is body of <b>HTML mail</b> without attachment<body></html>
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | HTML text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "HTML text external",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with inline image to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: Html Inline External
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Language: en-US
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
This is a multi-part message in MIME format.
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Html Inline External |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline External",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with alternative inline to internal account
When SMTP client "1" sends the following message from "user@pm.me" to "bridgetest@protonmail.com":
"""
From: Bridge Test <user@pm.me>
To: Internal Bridge <bridgetest@protonmail.com>
Subject: Html Inline Alternative Internal
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="------------5A259F4DE164B5ADA313F644"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------5A259F4DE164B5ADA313F644
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Behold! An inline
--------------5A259F4DE164B5ADA313F644
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
--------------5A259F4DE164B5ADA313F644--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | Html Inline Alternative Internal |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline Alternative Internal",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "bridgetest@protonmail.com",
"Name": "Internal Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with alternative inline to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: Html Inline Alternative External
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="------------5A259F4DE164B5ADA313F644"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------5A259F4DE164B5ADA313F644
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Behold! An inline
--------------5A259F4DE164B5ADA313F644
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
--------------5A259F4DE164B5ADA313F644--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Html Inline Alternative External |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline Alternative External",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with extremely long line (greater than default 2000 line limit) to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: HTML text external
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=utf-8
In-Reply-To: <base64hashOfSomeMessage@protonmail.internalid>
<html><body>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<body></html>
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | HTML text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "HTML text external",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""

View File

@ -19,8 +19,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | bridgetest@protonmail.com | | false |
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -54,8 +54,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -92,8 +92,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | bridgetest@protonmail.com | Plain text internal | false |
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | Plain text internal |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -130,8 +130,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -168,8 +168,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -209,8 +209,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -245,8 +245,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain, no charset, no content, external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain, no charset, no content, external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -285,8 +285,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | cc | subject | unread |
| user@pm.me | bridgetest@protonmail.com | bridgetest2@protonmail.com | RCPT-CC test | false |
| from | to | cc | subject |
| user@pm.me | bridgetest@protonmail.com | bridgetest2@protonmail.com | RCPT-CC test |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{

View File

@ -7,11 +7,11 @@ Feature: Address mode
| one | folder |
| two | folder |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And the address "alias@pm.me" of account "user@pm.me" has the following messages in "two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And bridge starts
@ -21,30 +21,30 @@ Feature: Address mode
Scenario: The user is in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "1" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "2" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -55,22 +55,22 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "1" sees 0 messages in "Folders/two"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees 0 messages in "Folders/one"
And IMAP client "2" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
@ -81,14 +81,14 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -97,14 +97,14 @@ Feature: Address mode
Scenario: The user adds an address while in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -113,7 +113,7 @@ Feature: Address mode
And bridge sends an address created event for user "user@pm.me"
When user "user@pm.me" connects and authenticates IMAP client "3" with address "other@pm.me"
Then IMAP client "3" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -124,12 +124,12 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
Given the account "user@pm.me" has additional address "other@pm.me"
@ -140,14 +140,14 @@ Feature: Address mode
Scenario: The user deletes an address while in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -162,12 +162,12 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
Given the account "user@pm.me" no longer has additional address "alias@pm.me"

View File

@ -7,11 +7,11 @@ Feature: Bridge can fully sync an account
| two | folder |
| three | label |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And bridge starts

View File

@ -127,15 +127,16 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfo(clientID string, table
return err
}
haveMailboxes := xslices.Map(status, func(info *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: info.Name,
Total: int(info.Messages),
Unread: int(info.Unseen),
}
haveMailboxes := xslices.Map(status, func(status *imap.MailboxStatus) Mailbox {
return newMailboxFromIMAP(status)
})
return matchMailboxes(haveMailboxes, table)
wantMailboxes, err := unmarshalTable[Mailbox](table)
if err != nil {
return err
}
return matchMailboxes(haveMailboxes, wantMailboxes)
}
func (s *scenario) imapClientEventuallySeesTheFollowingMailboxInfo(clientID string, table *godog.Table) error {
@ -159,14 +160,15 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mai
})
haveMailboxes := xslices.Map(status, func(info *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: info.Name,
Total: int(info.Messages),
Unread: int(info.Unseen),
}
return newMailboxFromIMAP(info)
})
return matchMailboxes(haveMailboxes, table)
wantMailboxes, err := unmarshalTable[Mailbox](table)
if err != nil {
return err
}
return matchMailboxes(haveMailboxes, wantMailboxes)
}
func (s *scenario) imapClientSeesTheFollowingMailboxes(clientID string, table *godog.Table) error {
@ -280,31 +282,15 @@ func (s *scenario) imapClientSeesTheFollowingMessagesInMailbox(clientID, mailbox
}
haveMessages := xslices.Map(fetch, func(msg *imap.Message) Message {
message := Message{
Subject: msg.Envelope.Subject,
Unread: slices.Contains(msg.Flags, imap.SeenFlag),
}
if len(msg.Envelope.From) > 0 {
message.From = msg.Envelope.From[0].Address()
}
if len(msg.Envelope.To) > 0 {
message.To = msg.Envelope.To[0].Address()
}
if len(msg.Envelope.Cc) > 0 {
message.CC = msg.Envelope.Cc[0].Address()
}
if len(msg.Envelope.Bcc) > 0 {
message.BCC = msg.Envelope.Bcc[0].Address()
}
return message
return newMessageFromIMAP(msg)
})
return matchMessages(haveMessages, table)
wantMessages, err := unmarshalTable[Message](table)
if err != nil {
return err
}
return matchMessages(haveMessages, wantMessages)
}
func (s *scenario) imapClientEventuallySeesTheFollowingMessagesInMailbox(clientID, mailbox string, table *godog.Table) error {

View File

@ -6,82 +6,85 @@ import (
"strconv"
"time"
"github.com/bradenaw/juniper/xslices"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/emersion/go-imap"
"golang.org/x/exp/slices"
)
type Message struct {
Subject string `bdd:"subject"`
From string `bdd:"sender"`
To string `bdd:"recipient"`
From string `bdd:"from"`
To string `bdd:"to"`
CC string `bdd:"cc"`
BCC string `bdd:"bcc"`
Unread bool `bdd:"unread"`
}
func newMessageFromRow(header, row *messages.PickleTableRow) Message {
var msg Message
if err := unmarshalRow(header, row, &msg); err != nil {
panic(err)
func newMessageFromIMAP(msg *imap.Message) Message {
message := Message{
Subject: msg.Envelope.Subject,
Unread: slices.Contains(msg.Flags, imap.SeenFlag),
}
return msg
if len(msg.Envelope.From) > 0 {
message.From = msg.Envelope.From[0].Address()
}
if len(msg.Envelope.To) > 0 {
message.To = msg.Envelope.To[0].Address()
}
if len(msg.Envelope.Cc) > 0 {
message.CC = msg.Envelope.Cc[0].Address()
}
if len(msg.Envelope.Bcc) > 0 {
message.BCC = msg.Envelope.Bcc[0].Address()
}
return message
}
func matchMessages(have []Message, want *godog.Table) error {
if want := parseMessages(want); !cmp.Equal(want, have, cmpopts.SortSlices(func(a, b Message) bool { return a.Subject < b.Subject })) {
return fmt.Errorf("want: %v, have: %v", want, have)
func matchMessages(have, want []Message) error {
if !IsSub(ToAny(have), ToAny(want)) {
return fmt.Errorf("missing messages: %v", want)
}
return nil
}
func parseMessages(table *godog.Table) []Message {
header := table.Rows[0]
return xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) Message {
return newMessageFromRow(header, row)
})
}
type Mailbox struct {
Name string `bdd:"name"`
Total int `bdd:"total"`
Unread int `bdd:"unread"`
}
func newMailboxFromRow(header, row *messages.PickleTableRow) Mailbox {
var mbox Mailbox
if err := unmarshalRow(header, row, &mbox); err != nil {
panic(err)
func newMailboxFromIMAP(status *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: status.Name,
Total: int(status.Messages),
Unread: int(status.Unseen),
}
return mbox
}
func matchMailboxes(have []Mailbox, want *godog.Table) error {
if want := parseMailboxes(want); !cmp.Equal(want, have, cmpopts.SortSlices(func(a, b Mailbox) bool { return a.Name < b.Name })) {
return fmt.Errorf("want: %v, have: %v", want, have)
func matchMailboxes(have, want []Mailbox) error {
slices.SortFunc(have, func(a, b Mailbox) bool {
return a.Name < b.Name
})
slices.SortFunc(want, func(a, b Mailbox) bool {
return a.Name < b.Name
})
if !IsSub(want, have) {
return fmt.Errorf("missing messages: %v", want)
}
return nil
}
func parseMailboxes(table *godog.Table) []Mailbox {
header := table.Rows[0]
return xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) Mailbox {
return newMailboxFromRow(header, row)
})
}
func eventually(condition func() error, waitFor, tick time.Duration) error {
ch := make(chan error, 1)
@ -111,14 +114,24 @@ func eventually(condition func() error, waitFor, tick time.Duration) error {
}
}
func getCellValue(header, row *messages.PickleTableRow, name string) (string, bool) {
for idx, cell := range header.Cells {
if cell.Value == name {
return row.Cells[idx].Value, true
}
func unmarshalTable[T any](table *messages.PickleTable) ([]T, error) {
if len(table.Rows) == 0 {
return nil, fmt.Errorf("empty table")
}
return "", false
var res []T
for _, row := range table.Rows[1:] {
var v T
if err := unmarshalRow(table.Rows[0], row, &v); err != nil {
return nil, err
}
res = append(res, v)
}
return res, nil
}
func unmarshalRow(header, row *messages.PickleTableRow, v any) error {
@ -152,6 +165,16 @@ func unmarshalRow(header, row *messages.PickleTableRow, v any) error {
return nil
}
func getCellValue(header, row *messages.PickleTableRow, name string) (string, bool) {
for idx, cell := range header.Cells {
if cell.Value == name {
return row.Cells[idx].Value, true
}
}
return "", false
}
func mustParseInt(s string) int {
i, err := strconv.Atoi(s)
if err != nil {

View File

@ -8,9 +8,7 @@ import (
"time"
"github.com/ProtonMail/gluon/rfc822"
"github.com/bradenaw/juniper/xslices"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/google/uuid"
"gitlab.protontech.ch/go/liteapi"
"golang.org/x/exp/slices"
@ -82,29 +80,28 @@ func (s *scenario) theAccountHasCustomLabels(username string, count int) error {
}
func (s *scenario) theAccountHasTheFollowingCustomMailboxes(username string, table *godog.Table) error {
type mailbox struct {
name string
typ liteapi.LabelType
type CustomMailbox struct {
Name string `bdd:"name"`
Type string `bdd:"type"`
}
wantMailboxes := xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) mailbox {
var mailboxType liteapi.LabelType
switch row.Cells[1].Value {
case "folder":
mailboxType = liteapi.LabelTypeFolder
case "label":
mailboxType = liteapi.LabelTypeLabel
}
return mailbox{
name: row.Cells[0].Value,
typ: mailboxType,
}
})
wantMailboxes, err := unmarshalTable[CustomMailbox](table)
if err != nil {
return err
}
for _, wantMailbox := range wantMailboxes {
if _, err := s.t.api.CreateLabel(s.t.getUserID(username), wantMailbox.name, wantMailbox.typ); err != nil {
var labelType liteapi.LabelType
switch wantMailbox.Type {
case "folder":
labelType = liteapi.LabelTypeFolder
case "label":
labelType = liteapi.LabelTypeLabel
}
if _, err := s.t.api.CreateLabel(s.t.getUserID(username), wantMailbox.Name, labelType); err != nil {
return err
}
}
@ -117,7 +114,12 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
addrID := s.t.getUserAddrID(userID, address)
mboxID := s.t.getMBoxID(userID, mailbox)
for _, wantMessage := range parseMessages(table) {
wantMessages, err := unmarshalTable[Message](table)
if err != nil {
return err
}
for _, wantMessage := range wantMessages {
if _, err := s.t.api.CreateMessage(
userID,
addrID,
@ -125,8 +127,8 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
wantMessage.Subject,
&mail.Address{Address: wantMessage.From},
[]*mail.Address{{Address: wantMessage.To}},
[]*mail.Address{},
[]*mail.Address{},
[]*mail.Address{{Address: wantMessage.CC}},
[]*mail.Address{{Address: wantMessage.BCC}},
"some body goes here",
rfc822.TextPlain,
wantMessage.Unread,