JAMES-2341 Demonstrate that Spam classification also works over IMAP

COPY to "Spam" mailbox should be supported as some clients uses a COPY + 
EXPUNGE mechanism to implement the MOVE.
MOVE extension should also be supported


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/04b3d79d
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/04b3d79d
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/04b3d79d

Branch: refs/heads/master
Commit: 04b3d79de77da83e70b26e1d9eb9986c51a25cc2
Parents: ce46d33
Author: benwa <[email protected]>
Authored: Wed Mar 7 11:44:40 2018 +0700
Committer: Antoine Duprat <[email protected]>
Committed: Thu Mar 8 11:01:18 2018 +0100

----------------------------------------------------------------------
 .../integration/SpamAssassinContract.java       | 103 +++++++++++++++++++
 .../apache/james/utils/IMAPMessageReader.java   |   4 +
 2 files changed, 107 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/04b3d79d/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java
index dc3f7be..6c75381 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java
@@ -37,6 +37,7 @@ import org.apache.james.jmap.HttpJmapAuthentication;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.mailbox.Role;
 import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.IMAPMessageReader;
 import org.apache.james.utils.JmapGuiceProbe;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -60,6 +61,8 @@ public interface SpamAssassinContract {
     String ALICES_DOMAIN = "angels.org";
     String ALICE = "alice@" + ALICES_DOMAIN;
     String ALICE_PASSWORD = "alicePassword";
+    String LOCALHOST = "127.0.0.1";
+    int IMAP_PORT = 1143;
 
     @BeforeEach
     default void setup(JamesWithSpamAssassin james) throws Throwable {
@@ -150,6 +153,106 @@ public interface SpamAssassinContract {
         calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2));
     }
 
+    @Test
+    default void 
imapCopiesToSpamMailboxShouldBeConsideredAsSpam(JamesWithSpamAssassin james) 
throws Exception {
+        Duration slowPacedPollInterval = Duration.FIVE_HUNDRED_MILLISECONDS;
+        ConditionFactory calmlyAwait = 
Awaitility.with().pollInterval(slowPacedPollInterval).and().with().pollDelay(slowPacedPollInterval).await();
+
+        james.getSpamAssassinExtension().getSpamAssassin().train(ALICE);
+        AccessToken aliceAccessToken = accessTokenFor(james.getJmapServer(), 
ALICE, ALICE_PASSWORD);
+        AccessToken bobAccessToken = accessTokenFor(james.getJmapServer(), 
BOB, BOB_PASSWORD);
+
+        // Bob is sending a message to Alice
+        given()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(setMessageCreate(bobAccessToken))
+        .when()
+            .post("/jmap");
+        calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getInboxId(aliceAccessToken), 1));
+
+        // Alice is moving this message to Spam -> learning in SpamAssassin
+        with()
+            .header("Authorization", aliceAccessToken.serialize())
+            .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + 
getInboxId(aliceAccessToken) + "\"]}}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", hasSize(1))
+            .extract()
+            .path(ARGUMENTS + ".messageIds");
+
+        try (IMAPMessageReader imapMessageReader = new IMAPMessageReader()) {
+            imapMessageReader.connect(LOCALHOST, IMAP_PORT)
+                .login(ALICE, ALICE_PASSWORD)
+                .select(IMAPMessageReader.INBOX);
+
+            imapMessageReader.copyFirstMessage("Spam");
+        }
+        calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 1));
+
+        // Bob is sending again the same message to Alice
+        given()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(setMessageCreate(bobAccessToken))
+        .when()
+            .post("/jmap");
+
+        // This message is delivered in Alice Spam mailbox (she now must have 
2 messages in her Spam mailbox)
+        calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2));
+    }
+
+    @Test
+    default void 
imapMovesToSpamMailboxShouldBeConsideredAsSpam(JamesWithSpamAssassin james) 
throws Exception {
+        Duration slowPacedPollInterval = Duration.FIVE_HUNDRED_MILLISECONDS;
+        ConditionFactory calmlyAwait = 
Awaitility.with().pollInterval(slowPacedPollInterval).and().with().pollDelay(slowPacedPollInterval).await();
+
+        james.getSpamAssassinExtension().getSpamAssassin().train(ALICE);
+        AccessToken aliceAccessToken = accessTokenFor(james.getJmapServer(), 
ALICE, ALICE_PASSWORD);
+        AccessToken bobAccessToken = accessTokenFor(james.getJmapServer(), 
BOB, BOB_PASSWORD);
+
+        // Bob is sending a message to Alice
+        given()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(setMessageCreate(bobAccessToken))
+        .when()
+            .post("/jmap");
+        calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getInboxId(aliceAccessToken), 1));
+
+        // Alice is moving this message to Spam -> learning in SpamAssassin
+        with()
+            .header("Authorization", aliceAccessToken.serialize())
+            .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + 
getInboxId(aliceAccessToken) + "\"]}}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", hasSize(1))
+            .extract()
+            .path(ARGUMENTS + ".messageIds");
+
+        try (IMAPMessageReader imapMessageReader = new IMAPMessageReader()) {
+            imapMessageReader.connect(LOCALHOST, IMAP_PORT)
+                .login(ALICE, ALICE_PASSWORD)
+                .select(IMAPMessageReader.INBOX);
+
+            imapMessageReader.moveFirstMessage("Spam");
+        }
+        calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 1));
+
+        // Bob is sending again the same message to Alice
+        given()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(setMessageCreate(bobAccessToken))
+        .when()
+            .post("/jmap");
+
+        // This message is delivered in Alice Spam mailbox (she now must have 
2 messages in her Spam mailbox)
+        calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> 
areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2));
+    }
+
     default boolean areMessagesFoundInMailbox(AccessToken accessToken, String 
mailboxId, int expectedNumberOfMessages) {
         try {
             with()

http://git-wip-us.apache.org/repos/asf/james-project/blob/04b3d79d/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java
----------------------------------------------------------------------
diff --git 
a/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java 
b/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java
index 277fe6d..2b7a7f0 100644
--- a/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java
+++ b/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java
@@ -168,4 +168,8 @@ public class IMAPMessageReader extends ExternalResource 
implements Closeable {
     public void copyFirstMessage(String destMailbox) throws IOException {
         imapClient.copy("1", destMailbox);
     }
+
+    public void moveFirstMessage(String destMailbox) throws IOException {
+        imapClient.sendCommand("MOVE 1 " + destMailbox);
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to