From e78cb8089b09b7c49705c607ea463f534868da19 Mon Sep 17 00:00:00 2001 From: Gjorgji Slamkov Date: Thu, 22 Jun 2023 05:13:53 +0000 Subject: [PATCH] test(GODT-2600): Changing state (read/unread, starred/unstarred) of a message in integration tests --- tests/bdd_test.go | 10 +- tests/features/imap/message/delete.feature | 2 +- tests/features/imap/message/state.feature | 59 ++++++++ tests/imap_test.go | 154 +++++++++++++++++++-- 4 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 tests/features/imap/message/state.feature diff --git a/tests/bdd_test.go b/tests/bdd_test.go index def2ac15..d0edb003 100644 --- a/tests/bdd_test.go +++ b/tests/bdd_test.go @@ -215,8 +215,16 @@ func TestFeatures(testingT *testing.T) { ctx.Step(`^IMAP client "([^"]*)" marks the message with subject "([^"]*)" as deleted$`, s.imapClientMarksTheMessageWithSubjectAsDeleted) ctx.Step(`^IMAP client "([^"]*)" marks message (\d+) as not deleted$`, s.imapClientMarksMessageAsNotDeleted) ctx.Step(`^IMAP client "([^"]*)" marks all messages as deleted$`, s.imapClientMarksAllMessagesAsDeleted) - ctx.Step(`^IMAP client "([^"]*)" sees that message (\d+) has the flag "([^"]*)"$`, s.imapClientSeesThatMessageHasTheFlag) ctx.Step(`^IMAP client "([^"]*)" expunges$`, s.imapClientExpunges) + ctx.Step(`^IMAP client "([^"]*)" marks message (\d+) as "([^"]*)"$`, s.imapClientMarksMessageAsState) + ctx.Step(`^IMAP client "([^"]*)" marks the message with subject "([^"]*)" as "([^"]*)"$`, s.imapClientMarksTheMessageWithSubjectAsState) + ctx.Step(`^IMAP client "([^"]*)" marks all messages as "([^"]*)"$`, s.imapClientMarksAllMessagesAsState) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that message at row (\d+) has the flag "([^"]*)"$`, s.imapClientSeesThatMessageHasTheFlag) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that message at row (\d+) does not have the flag "([^"]*)"$`, s.imapClientSeesThatMessageDoesNotHaveTheFlag) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that the message with subject "([^"]*)" has the flag "([^"]*)"`, s.imapClientSeesThatTheMessageWithSubjectHasTheFlag) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that the message with subject "([^"]*)" does not have the flag "([^"]*)"`, s.imapClientSeesThatTheMessageWithSubjectDoesNotHaveTheFlag) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that all the messages have the flag "([^"]*)"`, s.imapClientSeesThatAllTheMessagesHaveTheFlag) + ctx.Step(`^IMAP client "([^"]*)" eventually sees that all the messages do not have the flag "([^"]*)"`, s.imapClientSeesThatAllTheMessagesDoNotHaveTheFlag) ctx.Step(`^IMAP client "([^"]*)" appends the following message to "([^"]*)":$`, s.imapClientAppendsTheFollowingMessageToMailbox) ctx.Step(`^IMAP client "([^"]*)" appends the following messages to "([^"]*)":$`, s.imapClientAppendsTheFollowingMessagesToMailbox) ctx.Step(`^IMAP client "([^"]*)" appends "([^"]*)" to "([^"]*)"$`, s.imapClientAppendsToMailbox) diff --git a/tests/features/imap/message/delete.feature b/tests/features/imap/message/delete.feature index b9247f19..5e322924 100644 --- a/tests/features/imap/message/delete.feature +++ b/tests/features/imap/message/delete.feature @@ -18,7 +18,7 @@ Feature: IMAP remove messages from mailbox When IMAP client "1" selects "Folders/mbox" And IMAP client "1" marks message 2 as deleted And it succeeds - Then IMAP client "1" sees that message 2 has the flag "\Deleted" + Then IMAP client "1" eventually sees that message at row 2 has the flag "\Deleted" When IMAP client "1" expunges And it succeeds Then IMAP client "1" eventually sees 9 messages in "Folders/mbox" diff --git a/tests/features/imap/message/state.feature b/tests/features/imap/message/state.feature new file mode 100644 index 00000000..d1bf9b80 --- /dev/null +++ b/tests/features/imap/message/state.feature @@ -0,0 +1,59 @@ +Feature: IMAP change state of message in mailbox + Background: + Given there exists an account with username "[user:user]" and password "password" + And the account "[user:user]" has the following custom mailboxes: + | name | type | + | one | folder | + | two | folder | + And the address "[user:user]@[domain]" of account "[user:user]" has 5 messages in "Folders/one" + And the address "[user:user]@[domain]" of account "[user:user]" has 150 messages in "Folders/two" + And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Inbox": + | from | to | subject | unread | + | a@example.com | b@example.com | one | true | + | c@example.com | d@example.com | two | false | + And 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" + + Scenario: Mark message as read + When IMAP client "1" selects "Folders/one" + And IMAP client "1" marks message 1 as "read" + And it succeeds + Then IMAP client "1" eventually sees that message at row 1 has the flag "\Seen" + + Scenario: Mark message as unread + When IMAP client "1" selects "Folders/one" + And IMAP client "1" marks message 1 as "unread" + And it succeeds + Then IMAP client "1" eventually sees that message at row 1 does not have the flag "\Seen" + + Scenario: Mark message as starred + When IMAP client "1" selects "Folders/one" + And IMAP client "1" marks message 1 as "starred" + And it succeeds + Then IMAP client "1" eventually sees that message at row 1 has the flag "\Flagged" + + Scenario: Mark message as unstarred + When IMAP client "1" selects "Folders/one" + And IMAP client "1" marks message 1 as "unstarred" + And it succeeds + Then IMAP client "1" eventually sees that message at row 1 does not have the flag "\Flagged" + + Scenario: Mark message with subject as read/unread + When IMAP client "1" selects "Inbox" + And IMAP client "1" marks the message with subject "one" as "read" + And it succeeds + And IMAP client "1" marks the message with subject "two" as "unread" + And it succeeds + Then IMAP client "1" eventually sees that the message with subject "one" has the flag "\Seen" + And IMAP client "1" eventually sees that the message with subject "two" does not have the flag "\Seen" + + Scenario: Mark all messages in folder as read/unread + When IMAP client "1" selects "Folders/two" + And IMAP client "1" marks all messages as "read" + And it succeeds + Then IMAP client "1" eventually sees that all the messages have the flag "\Seen" + When IMAP client "1" marks all messages as "unread" + And it succeeds + Then IMAP client "1" eventually sees that all the messages do not have the flag "\Seen" diff --git a/tests/imap_test.go b/tests/imap_test.go index 7480e6d7..b8d6ceb9 100644 --- a/tests/imap_test.go +++ b/tests/imap_test.go @@ -401,25 +401,88 @@ func (s *scenario) imapClientMarksAllMessagesAsDeleted(clientID string) error { return nil } -func (s *scenario) imapClientSeesThatMessageHasTheFlag(clientID string, seq int, flag string) error { +func (s *scenario) imapClientMarksMessageAsState(clientID string, seq int, messageState string) error { _, client := s.t.getIMAPClient(clientID) - fetch, err := clientFetch(client, client.Mailbox().Name) + err := clientChangeMessageState(client, seq, messageState, true) + if err != nil { + s.t.pushError(err) + } + + return nil +} + +func (s *scenario) imapClientMarksTheMessageWithSubjectAsState(clientID, subject, messageState string) error { + _, client := s.t.getIMAPClient(clientID) + + uid, err := clientGetUIDBySubject(client, client.Mailbox().Name, subject) if err != nil { return err } - idx := xslices.IndexFunc(fetch, func(msg *imap.Message) bool { - return msg.SeqNum == uint32(seq) - }) - - if !slices.Contains(fetch[idx].Flags, flag) { - return fmt.Errorf("expected message %v to have flag %v, got %v", seq, flag, fetch[idx].Flags) + if err := clientChangeMessageState(client, int(uid), messageState, true); err != nil { + s.t.pushError(err) } return nil } +func (s *scenario) imapClientMarksAllMessagesAsState(clientID, messageState string) error { + _, client := s.t.getIMAPClient(clientID) + + if err := clientChangeAllMessageState(client, messageState); err != nil { + s.t.pushError(err) + } + + return nil +} + +func (s *scenario) imapClientSeesThatMessageHasTheFlag(clientID string, seq int, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + return clientIsFlagApplied(client, seq, flag, true, false) +} + +func (s *scenario) imapClientSeesThatMessageDoesNotHaveTheFlag(clientID string, seq int, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + return clientIsFlagApplied(client, seq, flag, false, false) +} + +func (s *scenario) imapClientSeesThatTheMessageWithSubjectHasTheFlag(clientID, subject, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + uid, err := clientGetUIDBySubject(client, client.Mailbox().Name, subject) + if err != nil { + return err + } + + return clientIsFlagApplied(client, int(uid), flag, true, false) +} + +func (s *scenario) imapClientSeesThatTheMessageWithSubjectDoesNotHaveTheFlag(clientID, subject, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + uid, err := clientGetUIDBySubject(client, client.Mailbox().Name, subject) + if err != nil { + return err + } + + return clientIsFlagApplied(client, int(uid), flag, false, false) +} + +func (s *scenario) imapClientSeesThatAllTheMessagesHaveTheFlag(clientID string, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + return clientIsFlagApplied(client, 1, flag, true, true) +} + +func (s *scenario) imapClientSeesThatAllTheMessagesDoNotHaveTheFlag(clientID string, flag string) error { + _, client := s.t.getIMAPClient(clientID) + + return clientIsFlagApplied(client, 1, flag, false, true) +} + func (s *scenario) imapClientExpunges(clientID string) error { _, client := s.t.getIMAPClient(clientID) @@ -767,3 +830,78 @@ func clientStore(client *client.Client, from, to int, isUID bool, item imap.Stor func clientAppend(client *client.Client, mailbox string, literal string) error { return client.Append(mailbox, []string{}, time.Now(), strings.NewReader(literal)) } + +func clientIsFlagApplied(client *client.Client, seq int, flag string, applied bool, wholeMailbox bool) error { + fetch, err := clientFetch(client, client.Mailbox().Name) + if err != nil { + return err + } + + idx := xslices.IndexFunc(fetch, func(msg *imap.Message) bool { + return msg.SeqNum == uint32(seq) + }) + + if slices.Contains(fetch[idx].Flags, flag) != applied { + return fmt.Errorf("expected message %v to have flag %v set to %v, got %v", seq, flag, applied, fetch[idx].Flags) + } + + if wholeMailbox { + for i := seq; i <= int(client.Mailbox().Messages); i++ { + idx := xslices.IndexFunc(fetch, func(msg *imap.Message) bool { + return msg.SeqNum == uint32(i) + }) + + if slices.Contains(fetch[idx].Flags, flag) != applied { + return fmt.Errorf("expected message %v to have flag %v set to %v, got %v", seq, flag, applied, fetch[idx].Flags) + } + } + } + + return nil +} + +func clientChangeMessageState(client *client.Client, seq int, messageState string, isUID bool) error { + switch { + case messageState == "read": + _, err := clientStore(client, seq, seq, isUID, imap.FormatFlagsOp(imap.AddFlags, true), imap.SeenFlag) + if err != nil { + return err + } + + case messageState == "unread": + _, err := clientStore(client, seq, seq, isUID, imap.FormatFlagsOp(imap.RemoveFlags, true), imap.SeenFlag) + if err != nil { + return err + } + + case messageState == "starred": + _, err := clientStore(client, seq, seq, isUID, imap.FormatFlagsOp(imap.AddFlags, true), imap.FlaggedFlag) + if err != nil { + return err + } + + case messageState == "unstarred": + _, err := clientStore(client, seq, seq, isUID, imap.FormatFlagsOp(imap.RemoveFlags, true), imap.FlaggedFlag) + if err != nil { + return err + } + } + + return nil +} + +func clientChangeAllMessageState(client *client.Client, messageState string) error { + if messageState == "read" { + _, err := clientStore(client, 1, int(client.Mailbox().Messages), false, imap.FormatFlagsOp(imap.AddFlags, true), imap.SeenFlag) + if err != nil { + return err + } + } else if messageState == "unread" { + _, err := clientStore(client, 1, int(client.Mailbox().Messages), false, imap.FormatFlagsOp(imap.RemoveFlags, true), imap.SeenFlag) + if err != nil { + return err + } + } + + return nil +}