chore: merge branch release/perth_narrows to devel

This commit is contained in:
Jakub
2023-02-15 13:13:08 +01:00
126 changed files with 4339 additions and 1484 deletions

View File

@ -162,7 +162,6 @@ func TestFeatures(testingT *testing.T) {
ctx.Step(`^user "([^"]*)" logs out$`, s.userLogsOut)
ctx.Step(`^user "([^"]*)" is deleted$`, s.userIsDeleted)
ctx.Step(`^the auth of user "([^"]*)" is revoked$`, s.theAuthOfUserIsRevoked)
ctx.Step(`^user "([^"]*)" is listed and connected$`, s.userIsListedAndConnected)
ctx.Step(`^user "([^"]*)" is eventually listed and connected$`, s.userIsEventuallyListedAndConnected)
ctx.Step(`^user "([^"]*)" is listed but not connected$`, s.userIsListedButNotConnected)
ctx.Step(`^user "([^"]*)" is not listed$`, s.userIsNotListed)
@ -184,7 +183,6 @@ func TestFeatures(testingT *testing.T) {
ctx.Step(`^IMAP client "([^"]*)" creates "([^"]*)"$`, s.imapClientCreatesMailbox)
ctx.Step(`^IMAP client "([^"]*)" deletes "([^"]*)"$`, s.imapClientDeletesMailbox)
ctx.Step(`^IMAP client "([^"]*)" renames "([^"]*)" to "([^"]*)"$`, s.imapClientRenamesMailboxTo)
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info:$`, s.imapClientSeesTheFollowingMailboxInfo)
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following mailbox info:$`, s.imapClientEventuallySeesTheFollowingMailboxInfo)
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info for "([^"]*)":$`, s.imapClientSeesTheFollowingMailboxInfoForMailbox)
ctx.Step(`^IMAP client "([^"]*)" sees "([^"]*)"$`, s.imapClientSeesMailbox)
@ -195,9 +193,7 @@ func TestFeatures(testingT *testing.T) {
ctx.Step(`^IMAP client "([^"]*)" copies all messages from "([^"]*)" to "([^"]*)"$`, s.imapClientCopiesAllMessagesFromTo)
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 "([^"]*)" sees the following messages in "([^"]*)":$`, s.imapClientSeesTheFollowingMessagesInMailbox)
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following messages in "([^"]*)":$`, s.imapClientEventuallySeesTheFollowingMessagesInMailbox)
ctx.Step(`^IMAP client "([^"]*)" sees (\d+) messages in "([^"]*)"$`, s.imapClientSeesMessagesInMailbox)
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 the message with subject "([^"]*)" as deleted$`, s.imapClientMarksTheMessageWithSubjectAsDeleted)

View File

@ -68,10 +68,10 @@ func (s *scenario) theUserChangesTheSMTPPortTo(port int) error {
func (s *scenario) theUserSetsTheAddressModeOfUserTo(user, mode string) error {
switch mode {
case "split":
return s.t.bridge.SetAddressMode(context.Background(), s.t.getUserID(user), vault.SplitMode)
return s.t.bridge.SetAddressMode(context.Background(), s.t.getUserByName(user).getUserID(), vault.SplitMode)
case "combined":
return s.t.bridge.SetAddressMode(context.Background(), s.t.getUserID(user), vault.CombinedMode)
return s.t.bridge.SetAddressMode(context.Background(), s.t.getUserByName(user).getUserID(), vault.CombinedMode)
default:
return fmt.Errorf("unknown address mode %q", mode)
@ -156,7 +156,7 @@ func (s *scenario) bridgeSendsADeauthEventForUser(username string) error {
return errors.New("expected deauth event, got none")
}
if wantUserID := s.t.getUserID(username); event.UserID != wantUserID {
if wantUserID := s.t.getUserByName(username).getUserID(); event.UserID != wantUserID {
return fmt.Errorf("expected deauth event for user %s, got %s", wantUserID, event.UserID)
}
@ -169,7 +169,7 @@ func (s *scenario) bridgeSendsAnAddressCreatedEventForUser(username string) erro
return errors.New("expected address created event, got none")
}
if wantUserID := s.t.getUserID(username); event.UserID != wantUserID {
if wantUserID := s.t.getUserByName(username).getUserID(); event.UserID != wantUserID {
return fmt.Errorf("expected address created event for user %s, got %s", wantUserID, event.UserID)
}
@ -182,7 +182,7 @@ func (s *scenario) bridgeSendsAnAddressDeletedEventForUser(username string) erro
return errors.New("expected address deleted event, got none")
}
if wantUserID := s.t.getUserID(username); event.UserID != wantUserID {
if wantUserID := s.t.getUserByName(username).getUserID(); event.UserID != wantUserID {
return fmt.Errorf("expected address deleted event for user %s, got %s", wantUserID, event.UserID)
}
@ -195,7 +195,7 @@ func (s *scenario) bridgeSendsSyncStartedAndFinishedEventsForUser(username strin
return errors.New("expected sync started event, got none")
}
if wantUserID := s.t.getUserID(username); startEvent.UserID != wantUserID {
if wantUserID := s.t.getUserByName(username).getUserID(); startEvent.UserID != wantUserID {
return fmt.Errorf("expected sync started event for user %s, got %s", wantUserID, startEvent.UserID)
}
@ -204,7 +204,7 @@ func (s *scenario) bridgeSendsSyncStartedAndFinishedEventsForUser(username strin
return errors.New("expected sync finished event, got none")
}
if wantUserID := s.t.getUserID(username); finishEvent.UserID != wantUserID {
if wantUserID := s.t.getUserByName(username).getUserID(); finishEvent.UserID != wantUserID {
return fmt.Errorf("expected sync finished event for user %s, got %s", wantUserID, finishEvent.UserID)
}

View File

@ -29,12 +29,14 @@ import (
"runtime"
"time"
"github.com/ProtonMail/gluon/imap"
"github.com/ProtonMail/gluon/queue"
"github.com/ProtonMail/proton-bridge/v3/internal/bridge"
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
"github.com/ProtonMail/proton-bridge/v3/internal/cookies"
"github.com/ProtonMail/proton-bridge/v3/internal/events"
frontend "github.com/ProtonMail/proton-bridge/v3/internal/frontend/grpc"
"github.com/ProtonMail/proton-bridge/v3/internal/service"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/sirupsen/logrus"
@ -160,6 +162,7 @@ func (t *testCtx) initBridge() (<-chan events.Event, error) {
t.mocks.ProxyCtl,
t.mocks.CrashHandler,
t.reporter,
imap.DefaultEpochUIDValidityGenerator(),
// Logging stuff
logIMAP,
@ -260,7 +263,7 @@ func (t *testCtx) initFrontendClient() error {
return fmt.Errorf("could not read grpcServerConfig.json: %w", err)
}
var cfg frontend.Config
var cfg service.Config
if err := json.Unmarshal(b, &cfg); err != nil {
return fmt.Errorf("could not unmarshal grpcServerConfig.json: %w", err)

View File

@ -20,6 +20,7 @@ package tests
import (
"context"
"fmt"
"os"
"runtime"
"github.com/ProtonMail/go-proton-api"
@ -33,6 +34,7 @@ func (t *testCtx) withProton(fn func(*proton.Manager) error) error {
proton.WithHostURL(t.api.GetHostURL()),
proton.WithTransport(proton.InsecureTransport()),
proton.WithAppVersion(t.api.GetAppVersion()),
proton.WithDebug(os.Getenv("FEATURE_API_DEBUG") != ""),
)
defer m.Close()
@ -41,7 +43,7 @@ func (t *testCtx) withProton(fn func(*proton.Manager) error) error {
// withClient executes the given function with a client that is logged in as the given (known) user.
func (t *testCtx) withClient(ctx context.Context, username string, fn func(context.Context, *proton.Client) error) error {
return t.withClientPass(ctx, username, t.getUserPass(t.getUserID(username)), fn)
return t.withClientPass(ctx, username, t.getUserByName(username).getUserPass(), fn)
}
// withClient executes the given function with a client that is logged in with the given username and password.
@ -106,7 +108,7 @@ func (t *testCtx) withAddrKR(
return err
}
keyPass, err := salt.SaltForKey([]byte(t.getUserPass(t.getUserID(username))), user.Keys.Primary().ID)
keyPass, err := salt.SaltForKey([]byte(t.getUserByName(username).getUserPass()), user.Keys.Primary().ID)
if err != nil {
return err
}
@ -122,14 +124,19 @@ func (t *testCtx) withAddrKR(
func (t *testCtx) createMessages(ctx context.Context, username, addrID string, req []proton.ImportReq) error {
return t.withClient(ctx, username, func(ctx context.Context, c *proton.Client) error {
return t.withAddrKR(ctx, c, username, addrID, func(ctx context.Context, addrKR *crypto.KeyRing) error {
if _, err := stream.Collect(ctx, c.ImportMessages(
str, err := c.ImportMessages(
ctx,
addrKR,
runtime.NumCPU(),
runtime.NumCPU(),
req...,
)); err != nil {
return err
)
if err != nil {
return fmt.Errorf("failed to prepare messages for import: %w", err)
}
if _, err := stream.Collect(ctx, str); err != nil {
return fmt.Errorf("failed to import messages: %w", err)
}
return nil

View File

@ -40,12 +40,85 @@ import (
"github.com/emersion/go-imap/client"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"google.golang.org/grpc"
)
var defaultVersion = semver.MustParse("3.0.6")
type testUser struct {
name string // the test user name
userID string // the user's account ID
addresses []*testAddr // the user's addresses
userPass string // the user's account password
bridgePass string // the user's bridge password
}
func newTestUser(userID, name, userPass string) *testUser {
return &testUser{
userID: userID,
name: name,
userPass: userPass,
}
}
func (user *testUser) getName() string {
return user.name
}
func (user *testUser) getUserID() string {
return user.userID
}
func (user *testUser) getEmails() []string {
return xslices.Map(user.addresses, func(addr *testAddr) string {
return addr.email
})
}
func (user *testUser) getAddrID(email string) string {
for _, addr := range user.addresses {
if addr.email == email {
return addr.addrID
}
}
panic(fmt.Sprintf("unknown email %q", email))
}
func (user *testUser) addAddress(addrID, email string) {
user.addresses = append(user.addresses, newTestAddr(addrID, email))
}
func (user *testUser) remAddress(addrID string) {
user.addresses = xslices.Filter(user.addresses, func(addr *testAddr) bool {
return addr.addrID != addrID
})
}
func (user *testUser) getUserPass() string {
return user.userPass
}
func (user *testUser) getBridgePass() string {
return user.bridgePass
}
func (user *testUser) setBridgePass(pass string) {
user.bridgePass = pass
}
type testAddr struct {
addrID string // the remote address ID
email string // the test address email
}
func newTestAddr(addrID, email string) *testAddr {
return &testAddr{
addrID: addrID,
email: email,
}
}
type testCtx struct {
// These are the objects supporting the test.
dir string
@ -70,13 +143,11 @@ type testCtx struct {
clientConn *grpc.ClientConn
clientEventCh *queue.QueuedChannel[*frontend.StreamEvent]
// These maps hold expected userIDByName, their primary addresses and bridge passwords.
userUUIDByName map[string]string
addrUUIDByName map[string]string
userIDByName map[string]string
userAddrByEmail map[string]map[string]string
userPassByID map[string]string
userBridgePassByID map[string][]byte
// These maps hold test objects created during the test.
userByID map[string]*testUser
userUUIDByName map[string]string
addrByID map[string]*testAddr
addrUUIDByName map[string]string
// These are the IMAP and SMTP clients used to connect to bridge.
imapClients map[string]*imapClient
@ -115,12 +186,10 @@ func newTestCtx(tb testing.TB) *testCtx {
events: newEventCollector(),
reporter: newReportRecorder(tb),
userUUIDByName: make(map[string]string),
addrUUIDByName: make(map[string]string),
userIDByName: make(map[string]string),
userAddrByEmail: make(map[string]map[string]string),
userPassByID: make(map[string]string),
userBridgePassByID: make(map[string][]byte),
userByID: make(map[string]*testUser),
userUUIDByName: make(map[string]string),
addrByID: make(map[string]*testAddr),
addrUUIDByName: make(map[string]string),
imapClients: make(map[string]*imapClient),
smtpClients: make(map[string]*smtpClient),
@ -192,62 +261,22 @@ func (t *testCtx) afterStep(st *godog.Step, status godog.StepResultStatus) {
logrus.Debugf("Finished step (%v): %s", status, st.Text)
}
func (t *testCtx) getName(wantUserID string) string {
for name, userID := range t.userIDByName {
if userID == wantUserID {
return name
func (t *testCtx) addUser(userID, name, userPass string) {
t.userByID[userID] = newTestUser(userID, name, userPass)
}
func (t *testCtx) getUserByName(name string) *testUser {
for _, user := range t.userByID {
if user.name == name {
return user
}
}
panic(fmt.Sprintf("unknown user ID %q", wantUserID))
panic(fmt.Sprintf("user %q not found", name))
}
func (t *testCtx) getUserID(username string) string {
return t.userIDByName[username]
}
func (t *testCtx) setUserID(username, userID string) {
t.userIDByName[username] = userID
}
func (t *testCtx) getUserAddrID(userID, email string) string {
return t.userAddrByEmail[userID][email]
}
func (t *testCtx) getUserAddrs(userID string) []string {
return maps.Keys(t.userAddrByEmail[userID])
}
func (t *testCtx) setUserAddr(userID, addrID, email string) {
if _, ok := t.userAddrByEmail[userID]; !ok {
t.userAddrByEmail[userID] = make(map[string]string)
}
t.userAddrByEmail[userID][email] = addrID
}
func (t *testCtx) unsetUserAddr(userID, wantAddrID string) {
for email, addrID := range t.userAddrByEmail[userID] {
if addrID == wantAddrID {
delete(t.userAddrByEmail[userID], email)
}
}
}
func (t *testCtx) getUserPass(userID string) string {
return t.userPassByID[userID]
}
func (t *testCtx) setUserPass(userID, pass string) {
t.userPassByID[userID] = pass
}
func (t *testCtx) getUserBridgePass(userID string) string {
return string(t.userBridgePassByID[userID])
}
func (t *testCtx) setUserBridgePass(userID string, pass []byte) {
t.userBridgePassByID[userID] = pass
func (t *testCtx) getUserByID(userID string) *testUser {
return t.userByID[userID]
}
func (t *testCtx) getMBoxID(userID string, name string) string {
@ -256,7 +285,7 @@ func (t *testCtx) getMBoxID(userID string, name string) string {
var labelID string
if err := t.withClient(ctx, t.getName(userID), func(ctx context.Context, client *proton.Client) error {
if err := t.withClient(ctx, t.getUserByID(userID).getName(), func(ctx context.Context, client *proton.Client) error {
labels, err := client.GetLabels(ctx, proton.LabelTypeLabel, proton.LabelTypeFolder, proton.LabelTypeSystem)
if err != nil {
panic(err)

View File

@ -37,7 +37,7 @@ Feature: IMAP create mailbox
Then it succeeds
When IMAP client "1" creates "Labels/l3"
Then it succeeds
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -65,7 +65,7 @@ Feature: IMAP create mailbox
Then it succeeds
When IMAP client "1" creates "Folders/f2/f22"
Then it succeeds
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -89,7 +89,7 @@ Feature: IMAP create mailbox
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 "2"
Then IMAP client "2" sees the following mailbox info:
Then IMAP client "2" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -119,7 +119,7 @@ Feature: IMAP create mailbox
Then it succeeds
When IMAP client "1" creates "Folders/f2/f22"
Then it succeeds
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -143,7 +143,7 @@ Feature: IMAP create mailbox
Then it succeeds
When IMAP client "1" renames "Folders/f1/f12" to "Folders/f2/f12"
Then it succeeds
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -167,7 +167,7 @@ Feature: IMAP create mailbox
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 "2"
Then IMAP client "2" sees the following mailbox info:
Then IMAP client "2" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |

View File

@ -7,7 +7,7 @@ Feature: IMAP Hide All Mail
And user "[user:user]" connects and authenticates IMAP client "1"
Scenario: Hide All Mail Mailbox
Given IMAP client "1" sees the following mailbox info:
Given IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -20,7 +20,7 @@ Feature: IMAP Hide All Mail
| Folders |
| Labels |
When the user hides All Mail
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -32,7 +32,7 @@ Feature: IMAP Hide All Mail
| Folders |
| Labels |
When the user shows All Mail
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |

View File

@ -9,7 +9,7 @@ Feature: IMAP list mailboxes
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" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name |
| INBOX |
| Drafts |
@ -36,4 +36,27 @@ Feature: IMAP list mailboxes
Then IMAP client "1" counts 20 mailboxes under "Folders"
And IMAP client "1" counts 60 mailboxes under "Labels"
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

@ -17,22 +17,22 @@ Feature: IMAP copy messages
Scenario: Copy message to label
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Labels/label"
And it succeeds
Then IMAP client "1" sees the following messages in "INBOX":
Then IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
And IMAP client "1" eventually sees the following messages in "Labels/label":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
Scenario: Copy all messages to label
When IMAP client "1" copies all messages from "INBOX" to "Labels/label"
And it succeeds
Then IMAP client "1" sees the following messages in "INBOX":
Then IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
And IMAP client "1" eventually sees the following messages in "Labels/label":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
@ -43,14 +43,14 @@ Feature: IMAP copy messages
Then IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "1" sees the following messages in "Folders/mbox":
And IMAP client "1" eventually sees the following messages in "Folders/mbox":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
Scenario: Copy all messages to folder does move
When IMAP client "1" copies all messages from "INBOX" to "Folders/mbox"
And it succeeds
Then IMAP client "1" sees the following messages in "Folders/mbox":
Then IMAP client "1" eventually sees the following messages in "Folders/mbox":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
@ -66,7 +66,7 @@ Feature: IMAP copy messages
And IMAP client "1" eventually sees 0 messages in "Sent"
Scenario: Copy message from All mail moves from the original location
Given IMAP client "1" sees the following messages in "INBOX":
Given IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |

View File

@ -12,10 +12,10 @@ Feature: IMAP create messages
| from | to | subject | body |
| john.doe@email.com | [user:user]@[domain] | foo | bar |
Then it succeeds
And IMAP client "1" sees the following messages in "INBOX":
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | body |
| john.doe@email.com | [user:user]@[domain] | foo | bar |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | body |
| john.doe@email.com | [user:user]@[domain] | foo | bar |
@ -37,10 +37,10 @@ Feature: IMAP create messages
| from | to | subject | body |
| [user:user]@[domain] | john.doe@email.com | foo | bar |
Then it succeeds
And IMAP client "1" sees the following messages in "Sent":
And IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | body |
| [user:user]@[domain] | john.doe@email.com | foo | bar |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | body |
| [user:user]@[domain] | john.doe@email.com | foo | bar |
@ -49,10 +49,10 @@ Feature: IMAP create messages
| from | to | subject | body |
| [alias:alias]@[domain] | john.doe@email.com | foo | bar |
Then it succeeds
And IMAP client "1" sees the following messages in "Sent":
And IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | body |
| [alias:alias]@[domain] | john.doe@email.com | foo | bar |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | body |
| [alias:alias]@[domain] | john.doe@email.com | foo | bar |
@ -61,10 +61,10 @@ Feature: IMAP create messages
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
Then it succeeds
And IMAP client "1" sees the following messages in "INBOX":
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
@ -73,10 +73,10 @@ Feature: IMAP create messages
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
Then it succeeds
And IMAP client "1" sees the following messages in "Sent":
And IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
@ -85,7 +85,7 @@ Feature: IMAP create messages
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
And it succeeds
And IMAP client "1" sees the following messages in "Sent":
And IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
And it succeeds
@ -93,7 +93,7 @@ Feature: IMAP create messages
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
And it succeeds
And IMAP client "1" sees the following messages in "Sent":
And IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | body |
| john.doe@email.com | john.doe2@[domain] | foo | bar |
| john.doe@email.com | john.doe2@[domain] | foo | bar |

View File

@ -6,6 +6,7 @@ Feature: IMAP remove messages from mailbox
| mbox | folder |
| 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 1 messages in "Scheduled"
And bridge starts
And the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing
@ -18,14 +19,14 @@ Feature: IMAP remove messages from mailbox
Then IMAP client "1" sees that message 2 has the flag "\Deleted"
When IMAP client "1" expunges
And it succeeds
Then IMAP client "1" sees 9 messages in "Folders/mbox"
Then IMAP client "1" eventually sees 9 messages in "Folders/mbox"
Scenario: Mark all messages as deleted and EXPUNGE
When IMAP client "1" selects "Folders/mbox"
And IMAP client "1" marks all messages as deleted
And IMAP client "1" expunges
And it succeeds
Then IMAP client "1" sees 0 messages in "Folders/mbox"
Then IMAP client "1" eventually sees 0 messages in "Folders/mbox"
Scenario: Mark messages as undeleted and EXPUNGE
When IMAP client "1" selects "Folders/mbox"
@ -37,11 +38,18 @@ Feature: IMAP remove messages from mailbox
And it succeeds
When IMAP client "1" expunges
And it succeeds
Then IMAP client "1" sees 2 messages in "Folders/mbox"
Then IMAP client "1" eventually sees 2 messages in "Folders/mbox"
Scenario: Not possible to delete from All Mail and expunge does nothing
When IMAP client "1" selects "All Mail"
And IMAP client "1" marks message 2 as deleted
And it succeeds
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

@ -20,14 +20,14 @@ Feature: IMAP remove messages from Trash
Then it succeeds
When IMAP client "1" marks the message with subject "foo" as deleted
Then it succeeds
And IMAP client "1" sees 2 messages in "Trash"
And IMAP client "1" sees 2 messages in "All Mail"
And IMAP client "1" sees 1 messages in "Labels/label"
And IMAP client "1" eventually sees 2 messages in "Trash"
And IMAP client "1" eventually sees 2 messages in "All Mail"
And IMAP client "1" eventually sees 1 messages in "Labels/label"
When IMAP client "1" expunges
Then it succeeds
And IMAP client "1" sees 1 messages in "Trash"
And IMAP client "1" sees 2 messages in "All Mail"
And IMAP client "1" sees 1 messages in "Labels/label"
And IMAP client "1" eventually sees 1 messages in "Trash"
And IMAP client "1" eventually sees 2 messages in "All Mail"
And IMAP client "1" eventually sees 1 messages in "Labels/label"
Scenario Outline: Message in Trash only is permanently deleted
Given the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Trash":
@ -41,9 +41,9 @@ Feature: IMAP remove messages from Trash
And IMAP client "1" selects "Trash"
When IMAP client "1" marks the message with subject "foo" as deleted
Then it succeeds
And IMAP client "1" sees 2 messages in "Trash"
And IMAP client "1" sees 2 messages in "All Mail"
And IMAP client "1" eventually sees 2 messages in "Trash"
And IMAP client "1" eventually sees 2 messages in "All Mail"
When IMAP client "1" expunges
Then it succeeds
And IMAP client "1" sees 1 messages in "Trash"
And IMAP client "1" eventually sees 1 messages in "Trash"
And IMAP client "1" eventually sees 1 messages in "All Mail"

View File

@ -15,7 +15,7 @@ Feature: IMAP Draft messages
Then IMAP client "1" eventually sees the following messages in "Drafts":
| body |
| This is a dra |
And IMAP client "1" sees 1 messages in "Drafts"
And IMAP client "1" eventually sees 1 messages in "Drafts"
Scenario: Draft edited locally
When IMAP client "1" marks message 1 as deleted
@ -33,7 +33,7 @@ Feature: IMAP Draft messages
And IMAP client "1" eventually sees the following messages in "Drafts":
| to | subject | body |
| someone@example.com | Basic Draft | This is a draft, but longer |
And IMAP client "1" sees 1 messages in "Drafts"
And IMAP client "1" eventually sees 1 messages in "Drafts"
Scenario: Draft edited remotely
When the following fields were changed in draft 1 for address "[user:user]@[domain]" of account "[user:user]":
@ -42,12 +42,12 @@ Feature: IMAP Draft messages
Then IMAP client "1" eventually sees the following messages in "Drafts":
| to | subject | body |
| someone@example.com | Basic Draft | This is a draft body, but longer |
And IMAP client "1" sees 1 messages in "Drafts"
And IMAP client "1" eventually sees 1 messages in "Drafts"
Scenario: Draft moved to trash remotely
When draft 1 for address "[user:user]@[domain]" of account "[user:user] was moved to trash
Then IMAP client "1" eventually sees the following messages in "Trash":
| body |
| This is a dra |
And IMAP client "1" sees 0 messages in "Drafts"
And IMAP client "1" eventually sees 0 messages in "Drafts"

View File

@ -13,7 +13,7 @@ Feature: IMAP Fetch
And user "[user:user]" connects and authenticates IMAP client "1"
Scenario: Fetch very old message
Given IMAP client "1" sees the following messages in "INBOX":
Given IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | date |
| john.doe@mail.com | [user:user]@[domain] | foo | 13 Aug 82 00:00 +0000 |
Then IMAP client "1" sees header "X-Original-Date: Sun, 13 Jul 1969 00:00:00 +0000" in message with subject "foo" in "INBOX"
@ -21,6 +21,6 @@ Feature: IMAP Fetch
Scenario: Fetch from deleted cache
When the user deletes the gluon cache
Then IMAP client "1" sees the following messages in "INBOX":
Then IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | date |
| john.doe@mail.com | [user:user]@[domain] | foo | 13 Aug 82 00:00 +0000 |

View File

@ -104,7 +104,7 @@ Feature: IMAP import messages
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" sees 0 messages in "Inbox"
And IMAP client "1" eventually sees 0 messages in "Inbox"
Scenario: Import non-received message to Inbox
When IMAP client "1" appends the following message to "Inbox":
@ -119,7 +119,7 @@ Feature: IMAP import messages
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | body |
| foo@example.com | bridgetest@pm.test | Hello | Hello |
And IMAP client "1" sees 0 messages in "Sent"
And IMAP client "1" eventually sees 0 messages in "Sent"
Scenario: Import non-received message to Sent
When IMAP client "1" appends the following message to "Sent":
@ -134,7 +134,7 @@ Feature: IMAP import messages
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" sees 0 messages in "Inbox"
And IMAP client "1" eventually sees 0 messages in "Inbox"
Scenario Outline: Import message without sender to <mailbox>
When IMAP client "1" appends the following message to "<mailbox>":

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":
| from | to | subject | unread |
| 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 the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing
@ -24,11 +27,11 @@ Feature: IMAP move messages
Scenario: Move message from folder to label (keeps in folder)
When IMAP client "1" moves the message with subject "foo" from "INBOX" to "Labels/label"
And it succeeds
And IMAP client "1" sees the following messages in "INBOX":
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
And IMAP client "1" eventually sees the following messages in "Labels/label":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
@ -41,28 +44,51 @@ Feature: IMAP move messages
And IMAP client "target" selects "Labels/label"
And IMAP clients "source" and "target" move message with subject "foo" of "[user:user]" to "Labels/label" by APPEND DELETE EXPUNGE
And it succeeds
Then IMAP client "source" sees the following messages in "INBOX":
Then IMAP client "source" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "target" sees the following messages in "Labels/label":
And IMAP client "target" eventually sees the following messages in "Labels/label":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
Scenario: Move message from label to folder
When IMAP client "1" moves the message with subject "baz" from "Labels/label2" to "Folders/mbox"
And it succeeds
And IMAP client "1" sees the following messages in "Folders/mbox":
And IMAP client "1" eventually sees the following messages in "Folders/mbox":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | baz | false |
And IMAP client "1" sees 0 messages in "Labels/label2"
And IMAP client "1" eventually sees 0 messages in "Labels/label2"
Scenario: Move message from label to label
When IMAP client "1" moves the message with subject "baz" from "Labels/label2" to "Labels/label"
And it succeeds
And IMAP client "1" sees the following messages in "Labels/label":
And IMAP client "1" eventually sees the following messages in "Labels/label":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | baz | false |
And IMAP client "1" sees 0 messages in "Labels/label2"
And IMAP client "1" eventually sees 0 messages in "Labels/label2"
Scenario: Move message from system label to system label
When IMAP client "1" moves the message with subject "foo" from "INBOX" to "Trash"
And it succeeds
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| jane.doe@mail.com | name@[domain] | bar | true |
And IMAP client "1" eventually sees the following messages in "Trash":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
Scenario: Move message from folder to system label
When IMAP client "1" moves the message with subject "baz" from "Labels/label2" to "Folders/mbox"
And it succeeds
And IMAP client "1" eventually sees the following messages in "Folders/mbox":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | baz | false |
When IMAP client "1" moves the message with subject "baz" from "Folders/mbox" to "Trash"
And it succeeds
And IMAP client "1" eventually sees 0 messages in "Folders/mbox"
And IMAP client "1" eventually sees the following messages in "Trash":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | baz | false |
Scenario: Move message from system label to system label
When IMAP client "1" moves the message with subject "foo" from "INBOX" to "Trash"
@ -90,14 +116,23 @@ Feature: IMAP move messages
Scenario: Move message from All Mail is not possible
When IMAP client "1" moves the message with subject "baz" from "All Mail" to "Folders/folder"
Then it fails
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |
| 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] | 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
When IMAP client "1" moves the message with subject "bar" from "Inbox" to "Sent"
Then it fails
@ -105,4 +140,4 @@ Feature: IMAP move messages
Scenario: Move message from Sent to Inbox is not possible
Given test skips reporter checks
When IMAP client "1" moves the message with subject "bax" from "Sent" to "Inbox"
Then it fails
Then it fails

View File

@ -34,8 +34,8 @@ Feature: IMAP move messages by append and delete (without MOVE support, e.g., Ou
And IMAP client "source" selects "<srcMailbox>"
And IMAP client "target" selects "<dstMailbox>"
When IMAP clients "source" and "target" move message with subject "subj2" of "[user:user]" to "<dstMailbox>" by <order>
And IMAP client "source" sees 1 messages in "<srcMailbox>"
And IMAP client "source" sees the following messages in "<srcMailbox>":
And IMAP client "source" eventually sees 1 messages in "<srcMailbox>"
And IMAP client "source" eventually sees the following messages in "<srcMailbox>":
| from | to | subject |
| sndr1@[domain] | rcvr1@[domain] | subj1 |
And IMAP client "target" eventually sees 1 messages in "<dstMailbox>"

View File

@ -17,7 +17,7 @@ Feature: Bridge can fully sync an account
Scenario: The user changes the gluon path
When the user changes the gluon path
And user "[user:user]" connects and authenticates IMAP client "2"
Then IMAP client "2" sees the following messages in "INBOX":
Then IMAP client "2" eventually sees the following messages in "INBOX":
| from | to | subject | unread |
| john.doe@mail.com | [user:user]@[domain] | foo | false |
| jane.doe@mail.com | name@[domain] | bar | true |

View File

@ -45,6 +45,7 @@ Feature: SMTP with bcc
"""
@long-black
Scenario: Send message only to bcc
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:bcc]@[domain]":
"""

View File

@ -7,6 +7,7 @@ Feature: SMTP sending embedded message
And the user logs in with username "[user:to]" and password "password"
And user "[user:user]" connects and authenticates SMTP client "1"
@long-black
Scenario: Send it
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
"""

View File

@ -6,6 +6,8 @@ Feature: SMTP sending two messages
And the user logs in with username "[user:user]" and password "password"
And the user logs in with username "[user:recp]" and password "password"
@long-black
Scenario: Send from one account to the other
When user "[user:user]" connects and authenticates SMTP client "1"
And SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:recp]@[domain]":
@ -60,6 +62,8 @@ Feature: SMTP sending two messages
| from | to | subject | body |
| [user:user]@[domain] | [user:recp]@[domain] | One account to the other | hello |
@long-black
Scenario: Send from one account to the other with attachments
When user "[user:user]" connects and authenticates SMTP client "1"
And SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:recp]@[domain]":

View File

@ -16,6 +16,7 @@ Feature: SMTP sending the same message twice
"""
And it succeeds
@long-black
Scenario: The exact same message is not sent twice
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
"""
@ -36,6 +37,7 @@ Feature: SMTP sending the same message twice
| [user:user]@[domain] | [user:to]@[domain] | Hello | World |
@long-black
Scenario: Slight change means different message and is sent twice
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:to]@[domain]":
"""

View File

@ -0,0 +1,153 @@
Feature: SMTP send reply
Background:
Given there exists an account with username "[user:user1]" and password "password"
And there exists an account with username "[user:user2]" and password "password"
And bridge starts
And the user logs in with username "[user:user1]" and password "password"
And user "[user:user1]" connects and authenticates SMTP client "1"
And user "[user:user1]" connects and authenticates IMAP client "1"
And user "[user:user1]" finishes syncing
@long-black
Scenario: Reply with In-Reply-To but no References
# User1 send the initial message.
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
"""
From: Bridge Test <[user:user1]@[domain]>
To: Internal Bridge <[user:user2]@[domain]>
Subject: Please Reply
Message-ID: <something@protonmail.ch>
hello
"""
Then it succeeds
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | message-id |
| [user:user1]@[domain] | [user:user2]@[domain] | Please Reply | <something@protonmail.ch> |
# login user2.
And the user logs in with username "[user:user2]" and password "password"
And user "[user:user2]" connects and authenticates IMAP client "2"
And user "[user:user2]" connects and authenticates SMTP client "2"
And user "[user:user2]" finishes syncing
# User2 receive the message.
Then IMAP client "2" eventually sees the following messages in "INBOX":
| from | subject | message-id |
| [user:user1]@[domain] | Please Reply | <something@protonmail.ch> |
# User2 reply to it.
When SMTP client "2" sends the following message from "[user:user2]@[domain]" to "[user:user1]@[domain]":
"""
From: Internal Bridge <[user:user2]@[domain]>
To: Bridge Test <[user:user1]@[domain]>
Content-Type: text/plain
Subject: FW - Please Reply
In-Reply-To: <something@protonmail.ch>
Heya
"""
Then it succeeds
Then IMAP client "2" eventually sees the following messages in "Sent":
| from | to | subject | in-reply-to | references |
| [user:user2]@[domain] | [user:user1]@[domain] | FW - Please Reply | <something@protonmail.ch> | <something@protonmail.ch> |
# User1 receive the reply.|
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | subject | body | in-reply-to | references |
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> |
@long-black
Scenario: Reply with References but no In-Reply-To
# User1 send the initial message.
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
"""
From: Bridge Test <[user:user1]@[domain]>
To: Internal Bridge <[user:user2]@[domain]>
Subject: Please Reply
Message-ID: <something@protonmail.ch>
hello
"""
Then it succeeds
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | message-id |
| [user:user1]@[domain] | [user:user2]@[domain] | Please Reply | <something@protonmail.ch> |
# login user2.
And the user logs in with username "[user:user2]" and password "password"
And user "[user:user2]" connects and authenticates IMAP client "2"
And user "[user:user2]" connects and authenticates SMTP client "2"
And user "[user:user2]" finishes syncing
# User2 receive the message.
Then IMAP client "2" eventually sees the following messages in "INBOX":
| from | subject | message-id |
| [user:user1]@[domain] | Please Reply | <something@protonmail.ch> |
# User2 reply to it.
When SMTP client "2" sends the following message from "[user:user2]@[domain]" to "[user:user1]@[domain]":
"""
From: Internal Bridge <[user:user2]@[domain]>
To: Bridge Test <[user:user1]@[domain]>
Content-Type: text/plain
Subject: FW - Please Reply
References: <something@protonmail.ch>
Heya
"""
Then it succeeds
Then IMAP client "2" eventually sees the following messages in "Sent":
| from | to | subject | in-reply-to | references |
| [user:user2]@[domain] | [user:user1]@[domain] | FW - Please Reply | <something@protonmail.ch> | <something@protonmail.ch> |
# User1 receive the reply.|
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | subject | body | in-reply-to | references |
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> |
@long-black
Scenario: Reply with both References and In-Reply-To
# User1 send the initial message.
When SMTP client "1" sends the following message from "[user:user1]@[domain]" to "[user:user2]@[domain]":
"""
From: Bridge Test <[user:user1]@[domain]>
To: Internal Bridge <[user:user2]@[domain]>
Subject: Please Reply
Message-ID: <something@protonmail.ch>
hello
"""
Then it succeeds
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject | message-id |
| [user:user1]@[domain] | [user:user2]@[domain] | Please Reply | <something@protonmail.ch> |
# login user2.
And the user logs in with username "[user:user2]" and password "password"
And user "[user:user2]" connects and authenticates IMAP client "2"
And user "[user:user2]" connects and authenticates SMTP client "2"
And user "[user:user2]" finishes syncing
# User2 receive the message.
Then IMAP client "2" eventually sees the following messages in "INBOX":
| from | subject | message-id |
| [user:user1]@[domain] | Please Reply | <something@protonmail.ch> |
# User2 reply to it.
When SMTP client "2" sends the following message from "[user:user2]@[domain]" to "[user:user1]@[domain]":
"""
From: Internal Bridge <[user:user2]@[domain]>
To: Bridge Test <[user:user1]@[domain]>
Content-Type: text/plain
Subject: FW - Please Reply
In-Reply-To: <something@protonmail.ch>
References: <something@protonmail.ch>
Heya
"""
Then it succeeds
Then IMAP client "2" eventually sees the following messages in "Sent":
| from | to | subject | in-reply-to | references |
| [user:user2]@[domain] | [user:user1]@[domain] | FW - Please Reply | <something@protonmail.ch> | <something@protonmail.ch> |
# User1 receive the reply.|
And IMAP client "1" eventually sees the following messages in "INBOX":
| from | subject | body | in-reply-to | references |
| [user:user2]@[domain] | FW - Please Reply | Heya | <something@protonmail.ch> | <something@protonmail.ch> |

View File

@ -20,30 +20,30 @@ Feature: Address mode
Scenario: The user is in combined mode
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
Then IMAP client "1" sees the following messages in "Folders/one":
Then IMAP client "1" eventually sees the following messages in "Folders/one":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
And IMAP client "1" sees the following messages in "Folders/two":
And IMAP client "1" eventually sees the following messages in "Folders/two":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
Then IMAP client "2" sees the following messages in "Folders/one":
Then IMAP client "2" eventually sees the following messages in "Folders/one":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
And IMAP client "2" sees the following messages in "Folders/two":
And IMAP client "2" eventually sees the following messages in "Folders/two":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
And IMAP client "2" sees the following messages in "All Mail":
And IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
@ -54,22 +54,22 @@ Feature: Address mode
Given the user sets the address mode of user "[user:user]" to "split"
And user "[user:user]" finishes syncing
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
Then IMAP client "1" sees the following messages in "Folders/one":
Then IMAP client "1" eventually sees the following messages in "Folders/one":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
And IMAP client "1" sees 0 messages in "Folders/two"
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees 0 messages in "Folders/two"
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
Then IMAP client "2" sees 0 messages in "Folders/one"
And IMAP client "2" sees the following messages in "Folders/two":
Then IMAP client "2" eventually sees 0 messages in "Folders/one"
And IMAP client "2" eventually sees the following messages in "Folders/two":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
And IMAP client "2" sees the following messages in "All Mail":
And IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
@ -80,14 +80,14 @@ Feature: Address mode
And the user sets the address mode of user "[user:user]" to "combined"
And user "[user:user]" finishes syncing
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
Then IMAP client "1" sees the following messages in "All Mail":
Then IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
Then IMAP client "2" sees the following messages in "All Mail":
Then IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
@ -96,14 +96,14 @@ Feature: Address mode
Scenario: The user adds an address while in combined mode
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
Then IMAP client "1" sees the following messages in "All Mail":
Then IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
Then IMAP client "2" sees the following messages in "All Mail":
Then IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
@ -112,7 +112,7 @@ Feature: Address mode
Given the account "[user:user]" has additional address "other@[domain]"
And bridge sends an address created event for user "[user:user]"
When user "[user:user]" connects and authenticates IMAP client "3" with address "other@[domain]"
Then IMAP client "3" sees the following messages in "All Mail":
Then IMAP client "3" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
@ -123,12 +123,12 @@ Feature: Address mode
Given the user sets the address mode of user "[user:user]" to "split"
And user "[user:user]" finishes syncing
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
And IMAP client "2" sees the following messages in "All Mail":
And IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
@ -139,14 +139,14 @@ Feature: Address mode
Scenario: The user deletes an address while in combined mode
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
Then IMAP client "1" sees the following messages in "All Mail":
Then IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
Then IMAP client "2" sees the following messages in "All Mail":
Then IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
@ -161,12 +161,12 @@ Feature: Address mode
Given the user sets the address mode of user "[user:user]" to "split"
And user "[user:user]" finishes syncing
When user "[user:user]" connects and authenticates IMAP client "1" with address "[user:user]@[domain]"
And IMAP client "1" sees the following messages in "All Mail":
And IMAP client "1" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| a@[domain] | a@[domain] | one | true |
| b@[domain] | b@[domain] | two | false |
When user "[user:user]" connects and authenticates IMAP client "2" with address "[alias:alias]@[domain]"
And IMAP client "2" sees the following messages in "All Mail":
And IMAP client "2" eventually sees the following messages in "All Mail":
| from | to | subject | unread |
| c@[domain] | c@[domain] | three | true |
| d@[domain] | d@[domain] | four | false |

View File

@ -7,15 +7,15 @@ Feature: A user can login
Scenario: Login to account
When the user logs in with username "[user:user]" and password "password"
Then user "[user:user]" is listed and connected
Then user "[user:user]" is eventually listed and connected
Scenario: Login to account with wrong password
When the user logs in with username "[user:user]" and password "wrong"
Then user "[user:user]" is not listed
Scenario: Login to nonexistent account
When the user logs in with username "[user:other]" and password "unknown"
Then user "[user:other]" is not listed
When the user logs in with username "nonexistent" and password "unknown"
Then user "nonexistent" is not listed
Scenario: Login to account without internet
Given the internet is turned off
@ -24,11 +24,11 @@ Feature: A user can login
Scenario: Login to account with caps
When the user logs in with username "[user:MixedCaps]" and password "password"
Then user "[user:MixedCaps]" is listed and connected
Then user "[user:MixedCaps]" is eventually listed and connected
Scenario: Login to account with disabled primary
When the user logs in with username "[user:disabled]" and password "password"
Then user "[user:disabled]" is listed and connected
Then user "[user:disabled]" is eventually listed and connected
Scenario: Login to account without internet but the connection is later restored
When the user logs in with username "[user:user]" and password "password"
@ -42,5 +42,5 @@ Feature: A user can login
Given there exists an account with username "[user:additional]" and password "password"
When the user logs in with username "[user:user]" and password "password"
And the user logs in with username "[user:additional]" and password "password"
Then user "[user:user]" is listed and connected
And user "[user:additional]" is listed and connected
Then user "[user:user]" is eventually listed and connected
And user "[user:additional]" is eventually listed and connected

View File

@ -8,7 +8,7 @@ Feature: A logged out user can login again
When user "[user:user]" logs out
And bridge restarts
And the user logs in with username "[user:user]" and password "password"
Then user "[user:user]" is listed and connected
Then user "[user:user]" is eventually listed and connected
Scenario: Cannot login to removed account
When user "[user:user]" is deleted

View File

@ -21,7 +21,7 @@ Feature: Bridge can fully sync an account
Then bridge sends sync started and finished events for user "[user:user]"
When bridge restarts
And user "[user:user]" connects and authenticates IMAP client "1"
Then IMAP client "1" sees the following mailbox info:
Then IMAP client "1" eventually sees the following mailbox info:
| name | total | unread |
| INBOX | 0 | 0 |
| Drafts | 0 | 0 |

View File

@ -38,35 +38,35 @@ import (
)
func (s *scenario) userConnectsIMAPClient(username, clientID string) error {
return s.t.newIMAPClient(s.t.getUserID(username), clientID)
return s.t.newIMAPClient(s.t.getUserByName(username).getUserID(), clientID)
}
func (s *scenario) userConnectsIMAPClientOnPort(username, clientID string, port int) error {
return s.t.newIMAPClientOnPort(s.t.getUserID(username), clientID, port)
return s.t.newIMAPClientOnPort(s.t.getUserByName(username).getUserID(), clientID, port)
}
func (s *scenario) userConnectsAndAuthenticatesIMAPClient(username, clientID string) error {
return s.userConnectsAndAuthenticatesIMAPClientWithAddress(username, clientID, s.t.getUserAddrs(s.t.getUserID(username))[0])
return s.userConnectsAndAuthenticatesIMAPClientWithAddress(username, clientID, s.t.getUserByName(username).getEmails()[0])
}
func (s *scenario) userConnectsAndAuthenticatesIMAPClientWithAddress(username, clientID, address string) error {
if err := s.t.newIMAPClient(s.t.getUserID(username), clientID); err != nil {
if err := s.t.newIMAPClient(s.t.getUserByName(username).getUserID(), clientID); err != nil {
return err
}
userID, client := s.t.getIMAPClient(clientID)
return client.Login(address, s.t.getUserBridgePass(userID))
return client.Login(address, s.t.getUserByID(userID).getBridgePass())
}
func (s *scenario) userConnectsAndCanNotAuthenticateIMAPClientWithAddress(username, clientID, address string) error {
if err := s.t.newIMAPClient(s.t.getUserID(username), clientID); err != nil {
if err := s.t.newIMAPClient(s.t.getUserByName(username).getUserID(), clientID); err != nil {
return err
}
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(address, s.t.getUserBridgePass(userID)); err == nil {
if err := client.Login(address, s.t.getUserByID(userID).getBridgePass()); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -76,19 +76,19 @@ func (s *scenario) userConnectsAndCanNotAuthenticateIMAPClientWithAddress(userna
func (s *scenario) imapClientCanAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
return client.Login(s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID))
return client.Login(s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass())
}
func (s *scenario) imapClientCanAuthenticateWithAddress(clientID string, address string) error {
userID, client := s.t.getIMAPClient(clientID)
return client.Login(address, s.t.getUserBridgePass(userID))
return client.Login(address, s.t.getUserByID(userID).getBridgePass())
}
func (s *scenario) imapClientCannotAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID)); err == nil {
if err := client.Login(s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass()); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -98,7 +98,7 @@ func (s *scenario) imapClientCannotAuthenticate(clientID string) error {
func (s *scenario) imapClientCannotAuthenticateWithAddress(clientID, address string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(address, s.t.getUserBridgePass(userID)); err == nil {
if err := client.Login(address, s.t.getUserByID(userID).getBridgePass()); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -108,7 +108,7 @@ func (s *scenario) imapClientCannotAuthenticateWithAddress(clientID, address str
func (s *scenario) imapClientCannotAuthenticateWithIncorrectUsername(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(s.t.getUserAddrs(userID)[0]+"bad", s.t.getUserBridgePass(userID)); err == nil {
if err := client.Login(s.t.getUserByID(userID).getEmails()[0]+"bad", s.t.getUserByID(userID).getBridgePass()); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -118,7 +118,7 @@ func (s *scenario) imapClientCannotAuthenticateWithIncorrectUsername(clientID st
func (s *scenario) imapClientCannotAuthenticateWithIncorrectPassword(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID)+"bad"); err == nil {
if err := client.Login(s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass()+"bad"); err == nil {
return fmt.Errorf("expected error, got nil")
}

View File

@ -27,25 +27,25 @@ import (
)
func (s *scenario) userConnectsSMTPClient(username, clientID string) error {
return s.t.newSMTPClient(s.t.getUserID(username), clientID)
return s.t.newSMTPClient(s.t.getUserByName(username).getUserID(), clientID)
}
func (s *scenario) userConnectsSMTPClientOnPort(username, clientID string, port int) error {
return s.t.newSMTPClientOnPort(s.t.getUserID(username), clientID, port)
return s.t.newSMTPClientOnPort(s.t.getUserByName(username).getUserID(), clientID, port)
}
func (s *scenario) userConnectsAndAuthenticatesSMTPClient(username, clientID string) error {
return s.userConnectsAndAuthenticatesSMTPClientWithAddress(username, clientID, s.t.getUserAddrs(s.t.getUserID(username))[0])
return s.userConnectsAndAuthenticatesSMTPClientWithAddress(username, clientID, s.t.getUserByName(username).getEmails()[0])
}
func (s *scenario) userConnectsAndAuthenticatesSMTPClientWithAddress(username, clientID, address string) error {
if err := s.t.newSMTPClient(s.t.getUserID(username), clientID); err != nil {
if err := s.t.newSMTPClient(s.t.getUserByName(username).getUserID(), clientID); err != nil {
return err
}
userID, client := s.t.getSMTPClient(clientID)
s.t.pushError(client.Auth(smtp.PlainAuth("", address, s.t.getUserBridgePass(userID), constants.Host)))
s.t.pushError(client.Auth(smtp.PlainAuth("", address, s.t.getUserByID(userID).getBridgePass(), constants.Host)))
return nil
}
@ -53,7 +53,7 @@ func (s *scenario) userConnectsAndAuthenticatesSMTPClientWithAddress(username, c
func (s *scenario) smtpClientCanAuthenticate(clientID string) error {
userID, client := s.t.getSMTPClient(clientID)
if err := client.Auth(smtp.PlainAuth("", s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID), constants.Host)); err != nil {
if err := client.Auth(smtp.PlainAuth("", s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass(), constants.Host)); err != nil {
return fmt.Errorf("expected no error, got %v", err)
}
@ -63,7 +63,7 @@ func (s *scenario) smtpClientCanAuthenticate(clientID string) error {
func (s *scenario) smtpClientCannotAuthenticate(clientID string) error {
userID, client := s.t.getSMTPClient(clientID)
if err := client.Auth(smtp.PlainAuth("", s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID), constants.Host)); err == nil {
if err := client.Auth(smtp.PlainAuth("", s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass(), constants.Host)); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -73,7 +73,7 @@ func (s *scenario) smtpClientCannotAuthenticate(clientID string) error {
func (s *scenario) smtpClientCannotAuthenticateWithIncorrectUsername(clientID string) error {
userID, client := s.t.getSMTPClient(clientID)
if err := client.Auth(smtp.PlainAuth("", s.t.getUserAddrs(userID)[0]+"bad", s.t.getUserBridgePass(userID), constants.Host)); err == nil {
if err := client.Auth(smtp.PlainAuth("", s.t.getUserByID(userID).getEmails()[0]+"bad", s.t.getUserByID(userID).getBridgePass(), constants.Host)); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -83,7 +83,7 @@ func (s *scenario) smtpClientCannotAuthenticateWithIncorrectUsername(clientID st
func (s *scenario) smtpClientCannotAuthenticateWithIncorrectPassword(clientID string) error {
userID, client := s.t.getSMTPClient(clientID)
if err := client.Auth(smtp.PlainAuth("", s.t.getUserAddrs(userID)[0], s.t.getUserBridgePass(userID)+"bad", constants.Host)); err == nil {
if err := client.Auth(smtp.PlainAuth("", s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass()+"bad", constants.Host)); err == nil {
return fmt.Errorf("expected error, got nil")
}

View File

@ -21,6 +21,7 @@ import (
"bytes"
"fmt"
"io"
"os"
"reflect"
"strconv"
"strings"
@ -49,6 +50,9 @@ type Message struct {
Unread bool `bdd:"unread"`
Deleted bool `bdd:"deleted"`
InReplyTo string `bdd:"in-reply-to"`
References string `bdd:"references"`
}
func (msg Message) Build() []byte {
@ -74,6 +78,14 @@ func (msg Message) Build() []byte {
b = append(b, "Subject: "+msg.Subject+"\r\n"...)
}
if msg.InReplyTo != "" {
b = append(b, "In-Reply-To: "+msg.InReplyTo+"\r\n"...)
}
if msg.References != "" {
b = append(b, "References: "+msg.References+"\r\n"...)
}
if msg.Date != "" {
date, err := time.Parse(time.RFC822, msg.Date)
if err != nil {
@ -125,6 +137,9 @@ func newMessageFromIMAP(msg *imap.Message) Message {
Unread: !slices.Contains(msg.Flags, imap.SeenFlag),
Deleted: slices.Contains(msg.Flags, imap.DeletedFlag),
Date: msg.Envelope.Date.Format(time.RFC822Z),
InReplyTo: msg.Envelope.InReplyTo,
// Go-imap only supports in-reply-to so we have to mimic other client by using it as references.
References: msg.Envelope.InReplyTo,
}
if len(msg.Envelope.From) > 0 {
@ -195,7 +210,13 @@ func matchMailboxes(have, want []Mailbox) error {
func eventually(condition func() error) error {
ch := make(chan error, 1)
timer := time.NewTimer(30 * time.Second)
var timerDuration = 30 * time.Second
// Extend to 5min for live API.
if hostURL := os.Getenv("FEATURE_TEST_HOST_URL"); hostURL != "" {
timerDuration = 600 * time.Second
}
timer := time.NewTimer(timerDuration)
defer timer.Stop()
ticker := time.NewTicker(100 * time.Millisecond)

View File

@ -32,7 +32,6 @@ import (
"github.com/bradenaw/juniper/xslices"
"github.com/cucumber/godog"
"github.com/google/uuid"
"golang.org/x/exp/slices"
)
func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, password string) error {
@ -52,7 +51,7 @@ func (s *scenario) theAccountHasAdditionalDisabledAddress(username, address stri
}
func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address string) error {
userID := s.t.getUserID(username)
userID := s.t.getUserByName(username).getUserID()
// Decrypt the user's encrypted ID for use with quark.
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID)
@ -65,7 +64,8 @@ func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address s
context.Background(),
"user:create:address",
string(userDecID),
s.t.getUserPass(userID),
s.t.getUserByID(userID).getUserPass(),
address,
); err != nil {
return err
@ -78,15 +78,15 @@ func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address s
}
// Set the new address of the user.
s.t.setUserAddr(userID, addr[len(addr)-1].ID, address)
s.t.getUserByID(userID).addAddress(addr[len(addr)-1].ID, address)
return nil
})
}
func (s *scenario) theAccountNoLongerHasAdditionalAddress(username, address string) error {
userID := s.t.getUserID(username)
addrID := s.t.getUserAddrID(userID, address)
userID := s.t.getUserByName(username).getUserID()
addrID := s.t.getUserByName(username).getAddrID(address)
if err := s.t.withClient(context.Background(), username, func(ctx context.Context, c *proton.Client) error {
if err := c.DisableAddress(ctx, addrID); err != nil {
@ -98,7 +98,7 @@ func (s *scenario) theAccountNoLongerHasAdditionalAddress(username, address stri
return err
}
s.t.unsetUserAddr(userID, addrID)
s.t.getUserByID(userID).remAddress(addrID)
return nil
}
@ -184,8 +184,8 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
userID := s.t.getUserID(username)
addrID := s.t.getUserAddrID(userID, address)
userID := s.t.getUserByName(username).getUserID()
addrID := s.t.getUserByName(username).getAddrID(address)
mboxID := s.t.getMBoxID(userID, mailbox)
wantMessages, err := unmarshalTable[Message](table)
@ -193,14 +193,6 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
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 proton.ImportReq{
@ -208,7 +200,7 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
AddressID: addrID,
LabelIDs: []string{mboxID},
Unread: proton.Bool(message.Unread),
Flags: messageFlags,
Flags: flagsForMailbox(mailbox),
},
Message: message.Build(),
}
@ -219,8 +211,8 @@ func (s *scenario) theAddressOfAccountHasMessagesInMailbox(address, username str
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
userID := s.t.getUserID(username)
addrID := s.t.getUserAddrID(userID, address)
userID := s.t.getUserByName(username).getUserID()
addrID := s.t.getUserByName(username).getAddrID(address)
mboxID := s.t.getMBoxID(userID, mailbox)
return s.t.createMessages(ctx, username, addrID, iterator.Collect(iterator.Map(iterator.Counter(count), func(idx int) proton.ImportReq {
@ -228,7 +220,7 @@ func (s *scenario) theAddressOfAccountHasMessagesInMailbox(address, username str
Metadata: proton.ImportMetadata{
AddressID: addrID,
LabelIDs: []string{mboxID},
Flags: proton.MessageFlagReceived,
Flags: flagsForMailbox(mailbox),
},
Message: Message{
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
// similar to sequential ID i.e. 1 represents the first message of draft folder
// sorted by API creation time.
@ -262,7 +266,7 @@ func (s *scenario) theFollowingFieldsWereChangedInDraftForAddressOfAccount(draft
defer cancel()
return s.t.withClient(ctx, username, func(ctx context.Context, c *proton.Client) error {
return s.t.withAddrKR(ctx, c, username, s.t.getUserAddrID(s.t.getUserID(username), address), func(_ context.Context, addrKR *crypto.KeyRing) error {
return s.t.withAddrKR(ctx, c, username, s.t.getUserByName(username).getAddrID(address), func(_ context.Context, addrKR *crypto.KeyRing) error {
var changes proton.DraftTemplate
if wantMessages[0].From != "" {
@ -311,7 +315,7 @@ func (s *scenario) drafAtIndexWasMovedToTrashForAddressOfAccount(draftIndex int,
defer cancel()
return s.t.withClient(ctx, username, func(ctx context.Context, c *proton.Client) error {
return s.t.withAddrKR(ctx, c, username, s.t.getUserAddrID(s.t.getUserID(username), address), func(_ context.Context, addrKR *crypto.KeyRing) error {
return s.t.withAddrKR(ctx, c, username, s.t.getUserByName(username).getAddrID(address), func(_ context.Context, addrKR *crypto.KeyRing) error {
if err := c.UnlabelMessages(ctx, []string{draftID}, proton.DraftsLabel); err != nil {
return fmt.Errorf("failed to unlabel draft")
}
@ -329,7 +333,7 @@ func (s *scenario) userLogsInWithUsernameAndPassword(username, password string)
if err != nil {
s.t.pushError(err)
} else {
if userID != s.t.getUserID(username) {
if userID != s.t.getUserByName(username).getUserID() {
return errors.New("user ID mismatch")
}
@ -338,18 +342,18 @@ func (s *scenario) userLogsInWithUsernameAndPassword(username, password string)
return err
}
s.t.setUserBridgePass(userID, info.BridgePass)
s.t.getUserByID(userID).setBridgePass(string(info.BridgePass))
}
return nil
}
func (s *scenario) userLogsOut(username string) error {
return s.t.bridge.LogoutUser(context.Background(), s.t.getUserID(username))
return s.t.bridge.LogoutUser(context.Background(), s.t.getUserByName(username).getUserID())
}
func (s *scenario) userIsDeleted(username string) error {
return s.t.bridge.DeleteUser(context.Background(), s.t.getUserID(username))
return s.t.bridge.DeleteUser(context.Background(), s.t.getUserByName(username).getUserID())
}
func (s *scenario) theAuthOfUserIsRevoked(username string) error {
@ -359,7 +363,7 @@ func (s *scenario) theAuthOfUserIsRevoked(username string) error {
}
func (s *scenario) userIsListedAndConnected(username string) error {
user, err := s.t.bridge.GetUserInfo(s.t.getUserID(username))
user, err := s.t.bridge.GetUserInfo(s.t.getUserByName(username).getUserID())
if err != nil {
return err
}
@ -382,7 +386,7 @@ func (s *scenario) userIsEventuallyListedAndConnected(username string) error {
}
func (s *scenario) userIsListedButNotConnected(username string) error {
user, err := s.t.bridge.GetUserInfo(s.t.getUserID(username))
user, err := s.t.bridge.GetUserInfo(s.t.getUserByName(username).getUserID())
if err != nil {
return err
}
@ -399,7 +403,7 @@ func (s *scenario) userIsListedButNotConnected(username string) error {
}
func (s *scenario) userIsNotListed(username string) error {
if slices.Contains(s.t.bridge.GetUserIDs(), s.t.getUserID(username)) {
if _, err := s.t.bridge.QueryUserInfo(username); !errors.Is(err, bridge.ErrNoSuchUser) {
return errors.New("user listed")
}
@ -411,7 +415,7 @@ func (s *scenario) userFinishesSyncing(username string) error {
}
func (s *scenario) addAdditionalAddressToAccount(username, address string, disabled bool) error {
userID := s.t.getUserID(username)
userID := s.t.getUserByName(username).getUserID()
// Decrypt the user's encrypted ID for use with quark.
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID)
@ -429,7 +433,7 @@ func (s *scenario) addAdditionalAddressToAccount(username, address string, disab
args = append(args,
string(userDecID),
s.t.getUserPass(userID),
s.t.getUserByID(userID).getUserPass(),
address,
)
@ -449,7 +453,7 @@ func (s *scenario) addAdditionalAddressToAccount(username, address string, disab
}
// Set the new address of the user.
s.t.setUserAddr(userID, addr[len(addr)-1].ID, address)
s.t.getUserByID(userID).addAddress(addr[len(addr)-1].ID, address)
return nil
})
@ -503,14 +507,11 @@ func (s *scenario) createUserAccount(username, password string, disabled bool) e
return err
}
// Set the ID of the user.
s.t.setUserID(username, user.ID)
// Set the password of the user.
s.t.setUserPass(user.ID, password)
// Add the test user.
s.t.addUser(user.ID, username, password)
// Set the address of the user.
s.t.setUserAddr(user.ID, addr[0].ID, addr[0].Email)
s.t.getUserByID(user.ID).addAddress(addr[0].ID, addr[0].Email)
return nil
})