io.stallion.plugins.flatBlog.comments.tests.TestCommentNotifications.java Source code

Java tutorial

Introduction

Here is the source code for io.stallion.plugins.flatBlog.comments.tests.TestCommentNotifications.java

Source

/*
 * Stallion Flat-file Blog: A simple blog-engine
 *
 * Copyright (C) 2015 - 2016 Stallion Software LLC
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details. You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 *
 */

package io.stallion.plugins.flatBlog.comments.tests;

import io.stallion.asyncTasks.AsyncCoordinator;
import io.stallion.asyncTasks.AsyncTask;
import io.stallion.asyncTasks.AsyncTaskController;
import io.stallion.asyncTasks.AsyncTaskExecuteRunnable;
import io.stallion.dataAccess.DataAccessRegistry;
import io.stallion.email.EmailSender;
import io.stallion.plugins.PluginRegistry;
import io.stallion.plugins.flatBlog.comments.CommentsEndpoints;
import io.stallion.plugins.flatBlog.comments.Comment;
import io.stallion.plugins.flatBlog.comments.CommentSubscriptionInfo;
import io.stallion.plugins.flatBlog.comments.NewCommentEmailHandler;
import io.stallion.plugins.flatBlog.contacts.*;
import io.stallion.plugins.flatBlog.FlatBlogPlugin;
import io.stallion.services.Log;
import io.stallion.testing.AppIntegrationCaseBase;
import io.stallion.testing.StubHandler;
import io.stallion.testing.Stubbing;
import io.stallion.utils.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static io.stallion.utils.Literals.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class TestCommentNotifications extends AppIntegrationCaseBase {
    private static Long parentId = 0L;
    private Contact john = null;
    private CommentsEndpoints resource = null;

    @BeforeClass
    public static void setUpClass() throws Exception {
        startApp("/blog_plugin_site");
        FlatBlogPlugin booter = new FlatBlogPlugin();
        PluginRegistry.instance().loadPluginFromBooter(booter);
        booter.boot();

        Stubbing.stub(CommentsEndpoints.class, "checkRECaptcha");
        Stubbing.stub(CommentsEndpoints.class, "checkAkismet");
        Stubbing.stub(NewCommentEmailHandler.class, "enqueue");

        parentId = DataAccessRegistry.instance().getTickets().nextId();

    }

    @Before
    public void setUp() throws Exception {
        resource = new CommentsEndpoints();
        resource.submitComment(newComment("test.john@stallion.io", "John Doe", "First!"));
        john = ContactsController.instance().forUniqueKeyOrNotFound("email", "test.john@stallion.io");
    }

    @Test
    public void testThreadSubscribe() throws Exception {
        CommentsEndpoints resource = new CommentsEndpoints();
        Long now = mils() - 2000;

        // Submit initial comment from Peter, no notifications yet.
        String peterEmail = "test.peter" + DateUtils.mils() + "@stallion.io";
        resource.submitComment(
                newComment(peterEmail, "Peter", "I eagerly await further thoughts!").setThreadSubscribe(true));
        Contact peter = ContactsController.instance().forUniqueKeyOrNotFound("email", peterEmail);
        Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", john.getId()).count());
        Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", peter.getId()).count());

        // Add two comments
        resource.submitComment(newComment("test.sam@stallion.io", "Sam", "This is an insightful thought."));
        resource.submitComment(newComment("test.shannon@stallion.io", "Shannon", "This is an incredible thing."));

        // Assert tasks and notifications created
        assertTrue(AsyncCoordinator.instance().getPendingTaskCount() > 0);
        List<Notification> notifications = NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", peter.getId()).all();
        Assert.assertEquals(2, notifications.size());
        Assert.assertEquals(notifications.get(0).getSendAt(), notifications.get(1).getSendAt());

        Notification notification = notifications.get(0);
        String customKey = "notification---" + notification.getSendAt() + "---" + notification.getContactId();
        Assert.assertTrue(AsyncCoordinator.instance().hasPendingTaskWithCustomKey(customKey));

        // Now try running the task for Peter, the user with a daily email
        // Should get 1 email with 2 comments in it

        MockEmailer mockEmailer = new MockEmailer();
        Stubbing.stub(EmailSender.class, "executeSend", mockEmailer);

        AsyncTask task = AsyncTaskController.instance().forUniqueKey("customKey", customKey);
        AsyncTaskExecuteRunnable runnable = new AsyncTaskExecuteRunnable(task);
        runnable.run(true);

        // Should be an email sent with two comments in it
        assertEquals(1, mockEmailer.getEmails().size());
        assertEquals(peterEmail, mockEmailer.getEmails().get(0).getAddress());
        Log.info("Notify email body: {0}", mockEmailer.getEmails().get(0).getBody());
        String body = mockEmailer.getEmails().get(0).getBody();
        assertTrue(body.contains("/my-subscriptions/"));
        assertTrue("/my-subscriptions/ contact token is empty", !body.contains("/my-subscriptions/\">"));

        assertEquals(2,
                StringUtils.countMatches(mockEmailer.getEmails().get(0).getBody(), "class=\"comment-bodyHtml\""));

    }

    @Test
    public void testMentionSubscribe() throws Exception {
        CommentsEndpoints resource = new CommentsEndpoints();
        Long now = mils() - 2000;

        resource.submitComment(newComment("test.jack@stallion.io", "Jack O'Connor",
                "I hope someone replies to me and mentions me!").setMentionSubscribe(true));

        Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", john.getId()).count());

        resource.submitComment(
                newComment("test.jamie@stallion.io", "Jamie", "Hey @\"Jack O'Connor\", I will reply to you."));

        resource.submitComment(newComment("test.jody@stallion.io", "Jody", "Hey @Jack, not a reply."));

        Contact jack = ContactsController.instance().forUniqueKeyOrNotFound("email", "test.jack@stallion.io");

        List<Notification> notifications = NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", jack.getId()).all();
        Assert.assertEquals(1, notifications.size());

        String customKey = "notification---" + notifications.get(0).getSendAt() + "---"
                + notifications.get(0).getContactId();
        assertTrue(AsyncCoordinator.instance().getPendingTaskCount() > 0);
        Assert.assertTrue(AsyncCoordinator.instance().hasPendingTaskWithCustomKey(customKey));

    }

    @Test
    public void testMentionEmails() throws Exception {
        CommentsEndpoints resource = new CommentsEndpoints();
        Long now = mils() - 2000;

        // Initial comment
        String timEmail = "test.tim." + now + "@stallion.io";
        resource.submitComment(newComment(timEmail, "Timothy", "I hope someone replies to me and mentions me!")
                .setMentionSubscribe(true));
        Contact tim = ContactsController.instance().forUniqueKeyOrNotFound("email", timEmail);
        // No notifications yet
        Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", john.getId()).count());
        Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", tim.getId()).count());

        // Two people reply to Timothy, one additional comment
        resource.submitComment(
                newComment("test.sherry@stallion.io", "Sherry", "Hey @Timothy, I will reply to you."));
        resource.submitComment(
                newComment("test.travis@stallion.io", "Travis", "Hey @Timothy, have a second reply."));
        resource.submitComment(newComment("test.mike@stallion.io", "Mike", "This is an unrelated comment."));

        // Two notifications, with the same send time
        List<Notification> notifications = NotificationController.instance().filter("createdAt", now, ">")
                .filter("contactId", tim.getId()).all();
        Assert.assertEquals(2, notifications.size());
        Assert.assertEquals(notifications.get(0).getSendAt(), notifications.get(1).getSendAt());

        // Verify pending task
        Notification notification = notifications.get(0);
        String customKey = "notification---" + notification.getSendAt() + "---" + notification.getContactId();
        assertTrue(AsyncCoordinator.instance().getPendingTaskCount() > 0);
        Assert.assertTrue(AsyncCoordinator.instance().hasPendingTaskWithCustomKey(customKey));

        // Now try running the task for 'three', the user with a daily email
        // Should get 1 email with 2 comments in it

        MockEmailer mockEmailer = new MockEmailer();
        Stubbing.stub(EmailSender.class, "executeSend", mockEmailer);

        AsyncTask task = AsyncTaskController.instance().forUniqueKey("customKey", customKey);
        AsyncTaskExecuteRunnable runnable = new AsyncTaskExecuteRunnable(task);
        runnable.run(true);

        // Should be an email sent with two comments in it
        assertEquals(1, mockEmailer.getEmails().size());
        assertEquals(timEmail, mockEmailer.getEmails().get(0).getAddress());
        Log.info("Notify email body: {0}", mockEmailer.getEmails().get(0).getBody());
        assertEquals(2,
                StringUtils.countMatches(mockEmailer.getEmails().get(0).getBody(), "class=\"comment-bodyHtml\""));

    }

    public Comment newComment(String email, String displayName, String bodyHtml) {
        Comment cmt = new Comment().setAuthorEmail(email).setAuthorDisplayName(displayName)
                .setBodyMarkdown(bodyHtml).setBodyHtml(bodyHtml);
        cmt.setParentTitle("Test Post");
        cmt.setThreadId(parentId);
        cmt.setModeratedAt(mils());
        cmt.setAkismetApproved(true);
        cmt.setParentPermalink("theparentpermalink");
        cmt.setMentionSubscribe(false);
        cmt.setThreadSubscribe(false);
        return cmt;
    }

    /*
    //@Test
    public void testNotifications() throws Exception {
    MockEmailer mockEmailer = new MockEmailer();
    Stubbing.stub(EmailSender.class, "executeSend", mockEmailer);
        
    Comment one = new Comment()
            .setAuthorEmail("testing+one@stallion.io")
            .setAuthorDisplayName("One")
            .setBodyHtml("A firsting comment.")
            ;
    CommentSubscriptionInfo oneSub = new CommentSubscriptionInfo()
            .setReplyNotifyFrequency(SubscriptionFrequency.INSTANT)
            .setThreadNotifyFrequency(SubscriptionFrequency.NEVER);
        
    Comment two = new Comment()
            .setAuthorEmail("testing+two@stallion.io")
            .setAuthorDisplayName("Two")
            .setBodyHtml("A seconding comment.");
    CommentSubscriptionInfo twoSub = new CommentSubscriptionInfo()
            .setReplyNotifyFrequency(SubscriptionFrequency.NEVER)
            .setThreadNotifyFrequency(SubscriptionFrequency.INSTANT);
        
    Comment three = new Comment()
            .setAuthorEmail("testing+three@stallion.io")
            .setAuthorDisplayName("Three")
            .setBodyHtml("A thirding comment.");
    CommentSubscriptionInfo threeSub = new CommentSubscriptionInfo()
            .setReplyNotifyFrequency(SubscriptionFrequency.DAILY)
            .setThreadNotifyFrequency(SubscriptionFrequency.DAILY);
        
        
    Comment four = new Comment()
            .setAuthorEmail("testing+four@stallion.io")
            .setAuthorDisplayName("Four")
            .setBodyHtml("A fourthing comment");
    CommentSubscriptionInfo fourSub = new CommentSubscriptionInfo()
            .setReplyNotifyFrequency(SubscriptionFrequency.NEVER)
            .setThreadNotifyFrequency(SubscriptionFrequency.NEVER);
        
        
    Comment five = new Comment()
            .setAuthorEmail("testing+five@stallion.io")
            .setAuthorDisplayName("Five")
            .setBodyHtml("A fifthing comment");
    CommentSubscriptionInfo fiveSub = new CommentSubscriptionInfo()
            .setReplyNotifyFrequency(SubscriptionFrequency.INSTANT)
            .setThreadNotifyFrequency(SubscriptionFrequency.NEVER);
    Comment threeAgain = new Comment()
            .setAuthorEmail("testing+three@stallion.io")
            .setAuthorDisplayName("Three")
            .setBodyHtml("A thirding comment, that happened again");
    Comment six = new Comment()
            .setAuthorEmail("testing+six@stallion.io")
            .setAuthorDisplayName("Six")
            .setBodyHtml("A sixing comment");
    Comment seven = new Comment()
            .setAuthorEmail("testing+seven@stallion.io")
            .setAuthorDisplayName("Seven")
            .setBodyHtml("A sevening comment A mention of @One");
        
    List<Comment> comments = list(one, two, three, four, five, threeAgain, six, seven);
        
    Long parentId = DataAccessRegistry.instance().getTickets().nextId();
    for (Comment cmt: comments) {
        cmt.setParentTitle("Test Post");
        cmt.setThreadId(parentId);
        cmt.setModeratedAt(mils());
        cmt.setAkismetApproved(true);
        cmt.setParentPermalink("theparentpermalink");
    }
    Long now = mils() - 2000;
        
    CommentsEndpoints resource = new CommentsEndpoints();
    /*
    one = resource.submitComment(one);
    resource.updateCommentSubscriptions(one, oneSub);
        
    assertEquals(0, AsyncCoordinator.instance().getPendingTaskCount());
    Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">").count());
        
    two = resource.submitComment(two);
    resource.updateCommentSubscriptions(two, twoSub);
    assertEquals(0, AsyncCoordinator.instance().getPendingTaskCount());
    Assert.assertEquals(0, NotificationController.instance().filter("createdAt", now, ">").count());
        
    three = resource.submitComment(three);
    resource.updateCommentSubscriptions(three, threeSub);
    // Two gets a task due to notification of comment Three
        
    assertEquals(1, AsyncCoordinator.instance().getPendingTaskCount());
    Assert.assertEquals(1, NotificationController.instance().filter("createdAt", now, ">").count());
    Notification notifyTwo = NotificationController.instance().filter("createdAt", now, ">").first();
        
    four = resource.submitComment(four);
    resource.updateCommentSubscriptions(four, fourSub);
    // Three gets a task due to comment Four
    //assertEquals(2, AsyncTaskController.instance().all().size());
        
    Notification notifyThree = NotificationController.instance().filter("contactId", three.getContactId().toString()).filter("createdAt", now, ">").first();
    // Two also gets a notification of comment four
    Assert.assertEquals(3, NotificationController.instance().filter("createdAt", now, ">").count());
        
    five = resource.submitComment(five);
    resource.updateCommentSubscriptions(five, fiveSub);
    // No new notifications, since four does not subscribe
    //assertEquals(2, AsyncTaskController.instance().all().size());
        
    // Two and Three get notifications, but no new task
    Assert.assertEquals(5, NotificationController.instance().filter("createdAt", now, ">").count());
        
    threeAgain = resource.submitComment(threeAgain);
    // No new tasks
    //assertEquals(2, AsyncTaskController.instance().all().size());
    assertEquals(6, NotificationController.instance().filter("createdAt", now, ">").count());
        
    six = resource.submitComment(six);
    // No new tasks
    //assertEquals(2, AsyncTaskController.instance().all().size());
    assertEquals(8, NotificationController.instance().filter("createdAt", now, ">").count());
        
    seven = resource.submitComment(seven);
    // One gets notified of comment Seven
    //assertEquals(3, AsyncTaskController.instance().all().size());
    assertEquals(11, NotificationController.instance().filter("createdAt", now, ">").count());
        
        
        
    // Now try running the task for 'three', the user with a daily email
    // Should get 1 email with 5 comments in it
    //AsyncTask threeTask = AsyncTaskController.instance().forUniqueKey("customKey", "notification---" + notifyThree.getSendAt() + "---" + notifyThree.getContactId());
    //AsyncTaskExecuteRunnable runnable = new AsyncTaskExecuteRunnable(threeTask);
    //runnable.run();
        
    /*
    assertEquals(1, mockEmailer.getEmails().size());
    assertEquals("testing+three@stallion.io", mockEmailer.getEmails().get(0).getAddress());
    Log.info("Notify email body: {0}", mockEmailer.getEmails().get(0).getBodyHtml());
    assertEquals(4, StringUtils.countMatches(mockEmailer.getEmails().get(0).getBodyHtml(), "class=\"comment-body\""));
        
        
    // Reset the mock emailer
    mockEmailer = new MockEmailer();
    Stubbing.stub(Emailer.class, "executeSend", mockEmailer);
        
        
    // Now try running one of the task for 'two', the commenter with an instant email
    // Should get 1 email with 5 comments in it
    //AsyncTask twoTask = AsyncTaskController.instance().forUniqueKey("customKey", "notification---" + notifyTwo.getSendAt() + "---" + notifyTwo.getContactId());
    //runnable = new AsyncTaskExecuteRunnable(twoTask);
    //runnable.run();
        
    assertEquals(1, mockEmailer.getEmails().size());
    assertEquals("testing+two@stallion.io", mockEmailer.getEmails().get(0).getAddress());
    Log.info("Notify email body: {0}", mockEmailer.getEmails().get(0).getBodyHtml());
    assertEquals(6, StringUtils.countMatches(mockEmailer.getEmails().get(0).getBodyHtml(), "class=\"comment-body\""));
        
    }
    */

    static class EmailInfo {
        private String address;
        private String body;

        public String getAddress() {
            return address;
        }

        public EmailInfo setAddress(String address) {
            this.address = address;
            return this;
        }

        public String getBody() {
            return body;
        }

        public EmailInfo setBody(String body) {
            this.body = body;
            return this;
        }
    }

    static class MockEmailer implements StubHandler {
        private List<EmailInfo> emails = new ArrayList<EmailInfo>();

        public Object execute(Object... params) throws Exception {
            EmailSender emailer = (EmailSender) params[0];
            getEmails().add(new EmailInfo().setAddress(emailer.getTos().get(0)).setBody(emailer.getHtml()));
            return null;
        }

        public List<EmailInfo> getEmails() {
            return emails;
        }

        public void setEmails(List<EmailInfo> emails) {
            this.emails = emails;
        }
    }
}