예제 #1
0
        private void SendMail(EventHandler <MailSenderAfterSendEventArgs> onAfterSend = null, EventHandler <MailSenderSmtpClientEventArgs> onSmtpConnected = null, EventHandler <MailSenderSmtpClientEventArgs> onSmtpDisconnected = null)
        {
            var data = new Dictionary <string, object>
            {
                { "MessageText", "This is just a sample plain text." },
                { "Date", DateTime.Now }
            };

            var mmm = new MailMergeMessage("Mailsubject sent on {Date}", "{MessageText}")
            {
                Config = _settings.MessageConfig
            };

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**"));

            var mms = new MailMergeSender()
            {
                Config = _settings.SenderConfig
            };

            mms.OnAfterSend        += onAfterSend;
            mms.OnSmtpConnected    += onSmtpConnected;
            mms.OnSmtpDisconnected += onSmtpDisconnected;

            mms.Send(mmm, (object)data);
        }
예제 #2
0
        public void TryToSendWhenSenderIsBusy()
        {
            var mmm = new MailMergeMessage("Subject", "plain text");

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**"));
            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name 2", "*****@*****.**"));

            var mms = new MailMergeSender
            {
                IsBusy = true
            };

            // single mail
            Assert.Throws <InvalidOperationException>(() => mms.Send(mmm, new object()));
            Assert.ThrowsAsync <InvalidOperationException>(async() => await mms.SendAsync(mmm, new object()));

            // several mails
            Assert.Throws <InvalidOperationException>(() => mms.Send(mmm, new Dictionary <string, string>()));
            Assert.ThrowsAsync <InvalidOperationException>(async() =>
                                                           await mms.SendAsync(mmm, new Dictionary <string, string>()));

            mms.IsBusy = false;
            mmm.Dispose();
            mms.Dispose();
        }
예제 #3
0
        //[TestCase(1000, Ignore = "Only for performance tests")]
        public async Task SendSyncAndAsyncPerformance(int numOfRecipients)
        {
            // In this sample:
            // With 100,000 messages and 10 MaxNumOfSmtpClients async is about twice as fast as sync.

            var recipients = new List <Recipient>();

            for (var i = 0; i < numOfRecipients; i++)
            {
                recipients.Add(new Recipient {
                    Email = $"recipient-{i}@example.com", Name = $"Name of {i}"
                });
            }

            var mmm = new MailMergeMessage("Async/Sync email test", "This is the plain text part for {Name} ({Email})")
            {
                Config = _settings.MessageConfig
            };

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));
            var mms = new MailMergeSender {
                Config = _settings.SenderConfig
            };

            mms.Config.MaxNumOfSmtpClients = 10;
            var sw = new Stopwatch();

            sw.Start();
            mms.Send(mmm, recipients);
            sw.Stop();
            Console.WriteLine($"Time to send {recipients.Count} messages sync: {sw.ElapsedMilliseconds} milliseconds.");
            Console.WriteLine();
            Assert.AreEqual(recipients.Count, _server.ReceivedEmail.Length);
            Assert.IsFalse(mms.IsBusy);

            sw.Reset();
            _server.ClearReceivedEmail();

            sw.Start();

            int numOfSmtpClientsUsed = 0;

            mms.OnMergeComplete += (s, args) => { numOfSmtpClientsUsed = args.NumOfSmtpClientsUsed; };

            await mms.SendAsync(mmm, recipients);

            sw.Stop();
            Console.WriteLine(
                $"Time to send {recipients.Count} messages async: {sw.ElapsedMilliseconds} milliseconds.");

            // Note: With too many SmtpClients and small emails some of the clients will never de-queue from the ConcurrentQueue of MailMergeSender
            Console.WriteLine(
                $"{numOfSmtpClientsUsed} tasks (and SmtpClients) used for sending async\n(max {mms.Config.MaxNumOfSmtpClients} were configured).");

            Assert.AreEqual(recipients.Count, _server.ReceivedEmail.Length);
            Assert.IsFalse(mms.IsBusy);

            mms.Dispose();
            mmm.Dispose();
        }
예제 #4
0
        public async Task CancelSendOperation()
        {
            var mmm = new MailMergeMessage("Cancel immediately", "plain text");

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**"));
            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "Test name 2", "*****@*****.**"));

            var mms = new MailMergeSender {
                Config = _settings.SenderConfig
            };

            mms.Config.MaxNumOfSmtpClients = 1;
            mms.Config.SmtpClientConfig[0].MessageOutput        = MessageOutput.SmtpServer;
            mms.Config.SmtpClientConfig[0].DelayBetweenMessages = 2000;

            var anyData = new[] { new { mailNo = 1 }, new { mailNo = 2 }, new { mailNo = 3 } };

            var tasks = new[]
            {
                await Task.Factory.StartNew(async() => await mms.SendAsync(mmm, anyData)),
                Task.Factory.StartNew(() => mms.SendCancel()),
                Task.Factory.StartNew(() => mms.SendCancel()) // a second cancel operation will just return
            };

            Assert.Throws <AggregateException>(() => { Task.WaitAll(tasks); });

            Assert.AreEqual(0, _server.ReceivedEmailCount);

            mms.Dispose();
            mmm.Dispose();
        }
예제 #5
0
        public void CancelSendOperationWithDelay()
        {
            var mmm = new MailMergeMessage("Cancel with delay", "plain text");

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "Test name", "*****@*****.**"));
            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "Test name 2", "*****@*****.**"));

            var mms = new MailMergeSender
            {
                Config = _settings.SenderConfig
            };

            mms.Config.MaxNumOfSmtpClients = 1;
            mms.Config.SmtpClientConfig[0].MessageOutput        = MessageOutput.SmtpServer;
            mms.Config.SmtpClientConfig[0].DelayBetweenMessages = 2000;

            var anyData = new[] { new { mailNo = 1 }, new { mailNo = 2 }, new { mailNo = 3 } };

            mms.SendCancel(500);
            Assert.ThrowsAsync <TaskCanceledException>(() => mms.SendAsync(mmm, anyData));
            Assert.AreEqual(0, _server.ReceivedEmailCount);

            mmm.Dispose();
            mms.Dispose();
        }
예제 #6
0
        public void TryToSendWithNullAsEnumerable()
        {
            var mms = new MailMergeSender();
            var mmm = new MailMergeMessage(); // no need to fully prepare for this test

            Assert.Throws <ArgumentNullException>(() => mms.Send(null, (Dictionary <string, string>)null));
            Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(mmm, (Dictionary <string, string>)null));
        }
예제 #7
0
        private MailMergeSender GetMailMergeSender()
        {
            var sender = new MailMergeSender {
                GetInitializedSmtpClientDelegate = config => new FakeSmtpClient()
            };

            return(sender);
        }
예제 #8
0
        public static MailMergeSender ConfigSender()
        {
            var sender = new MailMergeSender();

            sender.Config.MaxNumOfSmtpClients = 1;  // enough for a single email
            sender.Config.SmtpClientConfig[0].MessageOutput     = MessageOutput.SmtpServer;
            sender.Config.SmtpClientConfig[0].SmtpHost          = "smtp.mailprovider.net";
            sender.Config.SmtpClientConfig[0].SmtpPort          = 587;
            sender.Config.SmtpClientConfig[0].NetworkCredential = new Credential("username", "password");
            sender.Config.SmtpClientConfig[0].MaxFailures       = 3; // more throw an exception
            return(sender);
        }
예제 #9
0
        public void TryToSendWithNullMessage()
        {
            var mms = new MailMergeSender();

            // single mail
            Assert.Throws <ArgumentNullException>(() => mms.Send(null, new object()));
            Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(null, new object()));

            // several mails
            Assert.Throws <ArgumentNullException>(() => mms.Send(null, new Dictionary <string, string>()));
            Assert.ThrowsAsync <ArgumentNullException>(async() => await mms.SendAsync(null, new Dictionary <string, string>()));
        }
예제 #10
0
        public async Task AllSenderEvents()
        {
            var actualEvents   = new ConcurrentStack <string>();
            var expectedEvents = new ConcurrentStack <string>();

            var mms = new MailMergeSender {
                Config = _settings.SenderConfig
            };

            mms.Config.MaxNumOfSmtpClients = 1;

            // Event raising before merging starts
            mms.OnMergeBegin += (mailMergeSender, mergeBeginArgs) => { actualEvents.Push(nameof(mms.OnMergeBegin)); };
            // Event raising when getting the merged MimeMessage of the MailMergeMessage has failed.
            mms.OnMessageFailure += (mailMergeSender, messageFailureArgs) => { actualEvents.Push(nameof(mms.OnMessageFailure)); };

            // Event raising before sending a single mail message starts
            mms.OnBeforeSend += (smtpClient, beforeSendArgs) => { };

            // Event raising right after the SmtpClient's connection to the server is up (but not yet authenticated).
            mms.OnSmtpConnected += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpConnected)); };
            // Event raising after the SmtpClient has authenticated on the server.
            mms.OnSmtpAuthenticated += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpAuthenticated)); };
            // Event raising after the SmtpClient has disconnected from the SMTP mail server.
            mms.OnSmtpDisconnected += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpDisconnected)); };

            // Event raising if sending a single mail message fails
            mms.OnSendFailure += (smtpClient, sendFailureArgs) => { actualEvents.Push(nameof(mms.OnSendFailure)); };
            // Event raising before sending a single mail message is finished
            mms.OnAfterSend += (smtpClient, afterSendArgs) => { actualEvents.Push(nameof(mms.OnAfterSend)); };

            // Event raising each time before and after a single message was sent
            mms.OnMergeProgress += (mailMergeSender, progressArgs) => { actualEvents.Push(nameof(mms.OnMergeProgress)); };
            // Event raising after merging is completed
            mms.OnMergeComplete += (mailMergeSender, completedArgs) => { actualEvents.Push(nameof(mms.OnMergeComplete)); };

            var recipients = new List <Recipient>();

            for (var i = 0; i < 1; i++)
            {
                recipients.Add(new Recipient()
                {
                    Email = $"recipient-{i}@example.com", Name = $"Name of {i}"
                });
            }

            var mmm = new MailMergeMessage("Event tests", "This is the plain text part for {Name} ({Email})")
            {
                Config = _settings.MessageConfig
            };

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));

            var sequenceOfExpectedEvents = new[] { nameof(mms.OnMergeBegin), nameof(mms.OnMergeProgress), nameof(mms.OnSmtpConnected), nameof(mms.OnAfterSend), nameof(mms.OnMergeProgress), nameof(mms.OnMergeComplete), nameof(mms.OnSmtpDisconnected) };

            #region * Synchronous send method *

            mms.Send(mmm, recipients);

            expectedEvents.Clear();
            expectedEvents.PushRange(sequenceOfExpectedEvents);

            Assert.AreEqual(expectedEvents.Count, actualEvents.Count);
            // sequence of sync sending is predefined
            for (var i = 0; i < actualEvents.Count; i++)
            {
                expectedEvents.TryPop(out string expected);
                actualEvents.TryPop(out string actual);
                Assert.AreEqual(expected, actual);
            }

            #endregion

            #region * Async send method *

            actualEvents.Clear();
            expectedEvents.Clear();
            expectedEvents.PushRange(sequenceOfExpectedEvents);

            await mms.SendAsync(mmm, recipients);

            Assert.AreEqual(expectedEvents.Count, actualEvents.Count);

            // sequence of async sending may be different from sync, but all events must exists
            var sortedActual   = actualEvents.OrderBy(e => e).ToArray();
            var sortedExpected = expectedEvents.OrderBy(e => e).ToArray();

            for (var i = 0; i < sortedActual.Length; i++)
            {
                Assert.AreEqual(sortedExpected[i], sortedActual[i]);
            }

            #endregion
        }
예제 #11
0
        [TestCase(false, true)]  // no exception with null for MimeMessage must throw exception
        public void Send_With_And_Without_MailMergeMessageException(bool throwException, bool setMimeMessageToNull)
        {
            #region * Sync and Async preparation *

            const string theFormatError = "{causeFormatError}";
            const string plainText      = theFormatError + "This is the plain text part for {Name} ({Email})";

            var mms = new MailMergeSender {
                Config = _settings.SenderConfig
            };
            mms.Config.MaxNumOfSmtpClients = 1;

            // Event raising when getting the merged MimeMessage of the MailMergeMessage has failed.
            mms.OnMessageFailure += (mailMergeSender, messageFailureArgs) =>
            {
                lock (_locker)
                {
                    if (throwException)
                    {
                        return;
                    }

                    // Remove the cause of the exception and return corrected values
                    // Note: changes of MailMergeMessage will affect als messages to be sent
                    messageFailureArgs.MailMergeMessage.PlainText = plainText.Replace(theFormatError, string.Empty);
                    // in production a try...catch... must be implemented
                    if (setMimeMessageToNull)
                    {
                        messageFailureArgs.MimeMessage = null;
                    }
                    else
                    {
                        messageFailureArgs.MimeMessage =
                            messageFailureArgs.MailMergeMessage.GetMimeMessage(messageFailureArgs.DataSource);
                    }

                    messageFailureArgs.ThrowException = throwException;
                }
            };

            var recipients = new List <Recipient>();
            for (var i = 0; i < 2; i++)
            {
                recipients.Add(new Recipient {
                    Email = $"recipient-{i}@example.com", Name = $"Name of {i}"
                });
            }

            var mmm = new MailMergeMessage("Message failure", plainText)
            {
                Config = _settings.MessageConfig
            };
            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));

            #endregion

            #region * Synchronous send methods *

            // send enumerable data
            mmm.PlainText = plainText; // set text from constant
            try
            {
                if (throwException)
                {
                    Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients));
                }
                else
                {
                    if (setMimeMessageToNull)
                    {
                        Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients));
                    }
                    else
                    {
                        Assert.DoesNotThrow(() => mms.Send(mmm, recipients));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            if (throwException)
            {
                Assert.AreEqual(0, _server.ReceivedEmailCount);
            }
            else
            {
                if (setMimeMessageToNull)
                {
                    Assert.AreEqual(0, _server.ReceivedEmailCount);
                }
                else
                {
                    Assert.AreEqual(recipients.Count, _server.ReceivedEmailCount);
                }
            }

            _server.ClearReceivedEmail();

            // send single data item
            mmm.PlainText = plainText; // set text from constant
            try
            {
                if (throwException)
                {
                    Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients[0]));
                }
                else
                {
                    if (setMimeMessageToNull)
                    {
                        Assert.Throws <MailMergeMessage.MailMergeMessageException>(() => mms.Send(mmm, recipients[0]));
                    }
                    else
                    {
                        Assert.DoesNotThrow(() => mms.Send(mmm, recipients[0]));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            if (throwException)
            {
                Assert.AreEqual(0, _server.ReceivedEmailCount);
            }
            else
            {
                if (setMimeMessageToNull)
                {
                    Assert.AreEqual(0, _server.ReceivedEmailCount);
                }
                else
                {
                    Assert.AreEqual(1, _server.ReceivedEmailCount);
                }
            }

            _server.ClearReceivedEmail();

            #endregion

            #region * Async send methods *

            // send enumerable data
            mmm.PlainText = plainText; // set text from constant
            try
            {
                if (throwException)
                {
                    Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() =>
                                                                                    await mms.SendAsync(mmm, recipients));
                }
                else
                {
                    if (setMimeMessageToNull)
                    {
                        Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() =>
                                                                                        await mms.SendAsync(mmm, recipients));
                    }
                    else
                    {
                        Assert.DoesNotThrow(() => mms.Send(mmm, recipients));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            if (throwException)
            {
                Assert.AreEqual(0, _server.ReceivedEmailCount);
            }
            else
            {
                if (setMimeMessageToNull)
                {
                    Assert.AreEqual(0, _server.ReceivedEmailCount);
                }
                else
                {
                    Assert.AreEqual(recipients.Count, _server.ReceivedEmailCount);
                }
            }

            _server.ClearReceivedEmail();

            // send single data item
            mmm.PlainText = plainText; // set text from constant
            try
            {
                if (throwException)
                {
                    Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() =>
                                                                                    await mms.SendAsync(mmm, recipients[0]));
                }
                else
                {
                    if (setMimeMessageToNull)
                    {
                        Assert.ThrowsAsync <MailMergeMessage.MailMergeMessageException>(async() =>
                                                                                        await mms.SendAsync(mmm, recipients[0]));
                    }
                    else
                    {
                        Assert.DoesNotThrow(() => mms.Send(mmm, recipients[0]));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            if (throwException)
            {
                Assert.AreEqual(0, _server.ReceivedEmailCount);
            }
            else
            {
                if (setMimeMessageToNull)
                {
                    Assert.AreEqual(0, _server.ReceivedEmailCount);
                }
                else
                {
                    Assert.AreEqual(1, _server.ReceivedEmailCount);
                }
            }

            #endregion

            mms.Dispose();
            mmm.Dispose();
        }
예제 #12
0
        public async Task AllSenderEventsSingleMail(string somePlaceholder, bool withParseFailure)
        {
            #region * Sync and Async preparation *

            var actualEvents   = new ConcurrentStack <string>();
            var expectedEvents = new ConcurrentStack <string>();

            var mms = new MailMergeSender {
                Config = _settings.SenderConfig
            };
            mms.Config.MaxNumOfSmtpClients = 1;

            // Event raising when getting the merged MimeMessage of the MailMergeMessage has failed.
            mms.OnMessageFailure += (mailMergeSender, messageFailureArgs) =>
            {
                actualEvents.Push(nameof(mms.OnMessageFailure));
            };

            // Event raising before sending a single mail message starts
            mms.OnBeforeSend += (smtpClient, beforeSendArgs) => { actualEvents.Push(nameof(mms.OnBeforeSend)); };

            // Event raising right after the SmtpClient's connection to the server is up (but not yet authenticated).
            mms.OnSmtpConnected += (smtpClient, smtpClientArgs) => { actualEvents.Push(nameof(mms.OnSmtpConnected)); };
            // Event raising after the SmtpClient has authenticated on the server.
            mms.OnSmtpAuthenticated += (smtpClient, smtpClientArgs) =>
            {
                actualEvents.Push(nameof(mms.OnSmtpAuthenticated));
            };
            // Event raising after the SmtpClient has disconnected from the SMTP mail server.
            mms.OnSmtpDisconnected += (smtpClient, smtpClientArgs) =>
            {
                actualEvents.Push(nameof(mms.OnSmtpDisconnected));
            };

            // Event raising if sending a single mail message fails
            mms.OnSendFailure += (smtpClient, sendFailureArgs) => { actualEvents.Push(nameof(mms.OnSendFailure)); };
            // Event raising before sending a single mail message is finished
            mms.OnAfterSend += (smtpClient, afterSendArgs) => { actualEvents.Push(nameof(mms.OnAfterSend)); };

            var recipient = new Recipient {
                Email = $"*****@*****.**", Name = $"Name of recipient"
            };

            var mmm = new MailMergeMessage("Event tests" + somePlaceholder,
                                           "This is the plain text part for {Name} ({Email})")
            {
                Config = _settings.MessageConfig
            };

            mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));

            var sequenceOfExpectedEvents = new List <string>();
            if (withParseFailure)
            {
                sequenceOfExpectedEvents.Clear();
                sequenceOfExpectedEvents.AddRange(new[]
                {
                    nameof(mms.OnMessageFailure) /*,
                                                  * nameof(mms.OnSmtpDisconnected)*/
                });
            }
            else
            {
                sequenceOfExpectedEvents.Clear();
                sequenceOfExpectedEvents.AddRange(new[]
                {
                    nameof(mms.OnBeforeSend), nameof(mms.OnSmtpConnected),
                    nameof(mms.OnAfterSend), nameof(mms.OnSmtpDisconnected)
                });
            }

            #endregion

            #region * Synchronous send method *

            try
            {
                mms.Send(mmm, recipient);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            expectedEvents.Clear();
            expectedEvents.PushRange(sequenceOfExpectedEvents.ToArray());

            Assert.AreEqual(expectedEvents.Count, actualEvents.Count);
            // sequence of sync sending is predefined
            while (actualEvents.Count > 0)
            {
                expectedEvents.TryPop(out string expected);
                actualEvents.TryPop(out string actual);
                Assert.AreEqual(expected, actual);
            }

            #endregion

            #region * Async send method *

            actualEvents.Clear();
            expectedEvents.Clear();
            expectedEvents.PushRange(sequenceOfExpectedEvents.ToArray());

            try
            {
                await mms.SendAsync(mmm, recipient);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Assert.AreEqual(expectedEvents.Count, actualEvents.Count);

            // sequence of async sending may be different from sync, but all events must exists
            var sortedActual   = actualEvents.OrderBy(e => e).ToArray();
            var sortedExpected = expectedEvents.OrderBy(e => e).ToArray();

            for (var i = 0; i < sortedActual.Length; i++)
            {
                Assert.AreEqual(sortedExpected[i], sortedActual[i]);
            }

            #endregion

            mms.Dispose();
            mmm.Dispose();
        }