Skip to content

Add support for testing emails #3483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2021
Merged

Conversation

pietroalbini
Copy link
Member

Before this PR there wasn't really a practical way to test what the content of an email is, which makes it harder for example to test that the token included in the ownership confirmation email works with the relevant APIs.

The main thing this PR does is refactoring the email handling code, moving from standalone functions to an Emails struct (an instance of it is stored in the App). This allows to configure which email backend is used at startup instead of automatically detecting it every time we happen to send an email address. It will also allow to persist other email-related information when we'll need to.

Thanks to the new email backends support this PR also adds a "memory" backend, used during tests. Instead of sending the email via SMTP or storing it into a file, the "memory" backend stores it in a Mutex<Vec<_>> which can be inspected by any test.

Finally, to test the new email testing capabilities this PR adds a test to ensure a user can accept an ownership invitation with the token included in the email.

This PR is best reviewed commit-by-commit.
r? @jtgeibel or @Turbo87

Instead of having public functions dedicated to sending emails, this
commit switches to a Emails struct stored in the App. The struct holds
the backend configuration and state, and has methods for each email the
application can send.

Another use of the new struct is during tests: tests now use a new
"memory" backend, which stores all sent messages in a Mutex<Vec<_>>.
This allows tests to retrieve the mails sent by the code and run
assertions on them.

Finally, the send_* and try_send_* distinction was removed: all the
send_* methods now return an error, and it's the caller's job to swallow
the errors if they don't want to fail requests due to it.
@Turbo87 Turbo87 added C-internal 🔧 Category: Nonessential work that would make the codebase more consistent or clear A-backend ⚙️ labels Apr 2, 2021
Copy link
Member

@Turbo87 Turbo87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

just a few questions, but none of them are blocking

&email,
&req_user.gh_login,
&self.name,
&ownership_invitation.token,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense for us to at least log such errors?

@@ -97,7 +99,8 @@ impl<'a> NewUser<'a> {
.optional()?;

if let Some(token) = token {
crate::email::send_user_confirm_email(user_email, &user.gh_login, &token);
// Swallows any error. Some users might insert an invalid email address here.
let _ = emails.send_user_confirm(user_email, &user.gh_login, &token);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from a layering perspective it feels a bit wrong to me to have email sending code in the database model struct, but I guess refactoring this is out of scope for this PR

FileSystem { path: PathBuf },
/// Backend used during tests, will keep messages in memory to allow tests to retrieve them.
Memory { mails: Mutex<Vec<StoredEmail>> },
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what we're building here is essentially an interface with three backing implementations. I'm curious why you chose to implement that with an enum. would something like Box<dyn EmailTrait> work too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With few variants I personally prefer enums rather than a Box<dyn T>, but either could work just fine.

@Turbo87
Copy link
Member

Turbo87 commented Apr 4, 2021

@bors r+

@bors
Copy link
Contributor

bors commented Apr 4, 2021

📌 Commit 57cf070 has been approved by Turbo87

@bors
Copy link
Contributor

bors commented Apr 4, 2021

⌛ Testing commit 57cf070 with merge bea0a01...

@bors
Copy link
Contributor

bors commented Apr 4, 2021

☀️ Test successful - checks-actions
Approved by: Turbo87
Pushing bea0a01 to master...

@bors bors merged commit bea0a01 into rust-lang:master Apr 4, 2021
@pietroalbini pietroalbini deleted the email-tests branch April 6, 2021 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-backend ⚙️ C-internal 🔧 Category: Nonessential work that would make the codebase more consistent or clear
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants