Skip to content

Conversation

@yourcelf
Copy link
Contributor

Two commits to assist use in development: modify the test backend to use unique message ID's rather than a hardcoded 1. Also, add a console backend which provides anymail statuses and signal handling, while printing sent messages to the console.

Re Issue #85.

`EmailMultiAlternatives.message()` serializes an email message for
sending, and among other things adds a unique "Message-ID" header if one
isn't already present.  Use this in place of a hard-coded message ID in
the test backend.

For tests that inspect the message ID in the `anymail_status`, provide
an explicit "Message-ID" header rather than using the ephemeral
generated one.
Adds an EmailBackend derived from both Anymail's test backend and
Django's console backend, to provide anymail statuses and signal
handling while printing messages to the console.  For use during
development on localhost.
@medmunds
Copy link
Contributor

Thanks for this.

I'm concerned that it's misleading to pull the message_id from the email Message-ID header. Most ESPs generate a tracking message_id that's unrelated to the actual header Message-ID, so depending on having that header in the test backend could encourage code that would complicate changing ESPs. (Also, I think a lot of Anymail users don't bother setting their own Message-ID headers, instead letting their ESP generate them during the send.)

Would it work to generate a sequential or random message_id in the test backend?

Or maybe better for test reproducibility, generate a message_id based on len(mail.outbox), because Django clears that outbox at the same time it resets the test database.

@yourcelf
Copy link
Contributor Author

That makes sense. The EmailMultiAlternatives.message() generated message ID gets regenerated on every call to .message(); but mail.outbox stores the EmailMultiAlternatives instance itself and not the serialized output of .message(). As a result, a test can't use the generated output of .message() to predict the message ID that you'd get on a future call to .message(). That's why I provided the Message-ID header in the test case, to bypass that auto-generation.

Moving away from .message()'s implementation and doing something more deterministic could help avoid confusion in the test case, sure. Basing it on len(mail.outbox) would work in unit test contexts, but wouldn't ideal for localhost development, at least as long as you expect mesage IDs to be unique. Everytime you restart the dev server, len(mail.outbox) would be reset to 0, and then you'd be reusing message IDs again (and potentially running afoul of unique constraints).

Perhaps the implementation for generating a message ID really needs to be different between test and console/localhost development cases, where test message ID's are deterministic based on something like len(mail.outbox), but console message IDs are based on clock time like the current implementation. Would be easy to implement a get_message_id method on the test backend, and have the console backend which derives from it override that.

@medmunds
Copy link
Contributor

Yeah, none of the Anymail backends call .message()—the RFC-x822 format it generates is really only useful for communicating with SMTP servers, not ESP's REST APIs. (Also, that string won't include any of Anymail's custom props like tags or metadata. You might find it more helpful to print the Test backend's payload.params dict, which is everything Anymail knows about the message.)

If you want to do something different in the console backend to facilitate local development, you could override AnymailBaseBackend.pre_send_message() and/or .post_send_message(). (Be sure to chain to super, or the signal handlers won't get run.)

For the test EmailBackend, get message ID's based on array position in
`mail.outbox`, so that tests can predict the message ID.

For the console EmailBackend, use a uuid4 so that message ID's will be
globally unique.
@yourcelf
Copy link
Contributor Author

yourcelf commented Jan 28, 2018

I've added a commit to use different strategies for acquiring a message ID on the test and console backends.

The test backend uses the array position of the message in mail.outbox. This makes the message ID deterministic and predictable across tests without a need to specify the ID in advance, but ensures that ID's for multiple messages will remain unique within a single test.

The console backend just uses a uuid, ensuring global uniqueness for localhost development, while keeping the ESP message ID semantically distinct from the SMTP message ID.

@medmunds medmunds merged commit 771d404 into anymail:master Jan 28, 2018
@medmunds
Copy link
Contributor

Thanks!

@medmunds
Copy link
Contributor

Oops, closes #85.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants