blob: eeea375ba0a2dfd0ed2a459dca15f4a0069dc2d6 [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.jmap.mailet.filter;
import static org.apache.james.core.builder.MimeMessageBuilder.mimeMessageBuilder;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.CONTAINS;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.EXACTLY_EQUALS;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.NOT_CONTAINS;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.NOT_EXACTLY_EQUALS;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.CC;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.FROM;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.RECIPIENT;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.SUBJECT;
import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.TO;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.BOU;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.EMPTY;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULLNAME;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULL_SCRAMBLED_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.GA_BOU_ZO_MEU_FULL_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_MAILBOX_1;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_USERNAME;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SCRAMBLED_SUBJECT;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SHOULD_NOT_MATCH;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNFOLDED_USERNAME;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNSCRAMBLED_SUBJECT;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_AND_UNFOLDED_USER_FULL_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_FULL_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_USERNAME;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_FULL_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_USERNAME;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_FULL_ADDRESS;
import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_4_FULL_ADDRESS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;
import jakarta.mail.Flags;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.core.Username;
import org.apache.james.core.builder.MimeMessageBuilder;
import org.apache.james.jmap.api.filtering.Rule;
import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
import org.apache.james.jmap.mailet.filter.JMAPFilteringExtension.JMAPFilteringTestSystem;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.util.StreamUtils;
import org.apache.mailet.Attribute;
import org.apache.mailet.AttributeName;
import org.apache.mailet.AttributeValue;
import org.apache.mailet.StorageDirective;
import org.apache.mailet.base.RFC2822Headers;
import org.apache.mailet.base.test.FakeMail;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import com.github.fge.lambdas.Throwing;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import reactor.core.publisher.Mono;
@ExtendWith(JMAPFilteringExtension.class)
class JMAPFilteringTest {
private static final AttributeName RECIPIENT_1_USERNAME_ATTRIBUTE_NAME = AttributeName.of("DeliveryPaths_" + RECIPIENT_1_USERNAME.asString());
private static final Attribute RECIPIENT_1_MAILBOX_1_ATTRIBUTE = new Attribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME, AttributeValue.of(ImmutableList.of(RECIPIENT_1_MAILBOX_1)));
static class FilteringArgumentBuilder {
private Optional<String> description;
private Optional<Rule.Condition.Field> field;
private MimeMessageBuilder mimeMessageBuilder;
private Optional<String> valueToMatch;
private FilteringArgumentBuilder() {
this.description = Optional.empty();
this.field = Optional.empty();
mimeMessageBuilder = MimeMessageBuilder.mimeMessageBuilder();
this.valueToMatch = Optional.empty();
}
public FilteringArgumentBuilder description(String description) {
this.description = Optional.ofNullable(description);
return this;
}
public FilteringArgumentBuilder field(Rule.Condition.Field field) {
this.field = Optional.ofNullable(field);
return this;
}
public FilteringArgumentBuilder from(String from) {
Optional.ofNullable(from).ifPresent(Throwing.consumer(mimeMessageBuilder::addFrom));
return this;
}
public FilteringArgumentBuilder noHeader() {
return this;
}
public FilteringArgumentBuilder toRecipient(String toRecipient) {
Optional.ofNullable(toRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addToRecipient));
return this;
}
public FilteringArgumentBuilder ccRecipient(String ccRecipient) {
Optional.ofNullable(ccRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addCcRecipient));
return this;
}
public FilteringArgumentBuilder bccRecipient(String bccRecipient) {
Optional.ofNullable(bccRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addBccRecipient));
return this;
}
public FilteringArgumentBuilder header(String headerName, String headerValue) {
mimeMessageBuilder.addHeader(headerName, headerValue);
return this;
}
public FilteringArgumentBuilder subject(String subject) {
mimeMessageBuilder.setSubject(subject);
return this;
}
public FilteringArgumentBuilder valueToMatch(String valueToMatch) {
this.valueToMatch = Optional.ofNullable(valueToMatch);
return this;
}
public FilteringArgumentBuilder scrambledSubjectToMatch(String valueToMatch) {
return description("normal content")
.field(SUBJECT)
.subject(SCRAMBLED_SUBJECT)
.valueToMatch(valueToMatch);
}
public FilteringArgumentBuilder scrambledSubjectShouldNotMatchCaseSensitive() {
return description("normal content (case sensitive)")
.field(SUBJECT)
.subject(SCRAMBLED_SUBJECT)
.valueToMatch(SCRAMBLED_SUBJECT.toUpperCase(Locale.FRENCH));
}
public FilteringArgumentBuilder unscrambledSubjectToMatch(String valueToMatch) {
return description("unscrambled content")
.field(SUBJECT)
.subject(UNSCRAMBLED_SUBJECT)
.valueToMatch(valueToMatch);
}
public FilteringArgumentBuilder unscrambledSubjectShouldNotMatchCaseSensitive() {
return description("unscrambled content (case sensitive)")
.field(SUBJECT)
.subject(UNSCRAMBLED_SUBJECT)
.valueToMatch(UNSCRAMBLED_SUBJECT.toUpperCase(Locale.FRENCH));
}
public FilteringArgumentBuilder testForUpperCase() {
return description(description.get() + " (different case)")
.valueToMatch(valueToMatch.get().toUpperCase(Locale.US));
}
public Arguments build() {
Preconditions.checkState(description.isPresent());
Preconditions.checkState(field.isPresent());
Preconditions.checkState(valueToMatch.isPresent());
return Arguments.of(description.get(), field.get(), mimeMessageBuilder, valueToMatch.get());
}
}
static Stream<Arguments> forBothCase(FilteringArgumentBuilder builder) {
return Stream.of(
builder.build(),
builder.testForUpperCase().build());
}
static FilteringArgumentBuilder argumentBuilder() {
return new FilteringArgumentBuilder();
}
static FilteringArgumentBuilder argumentBuilder(Rule.Condition.Field field) {
return new FilteringArgumentBuilder()
.field(field);
}
static class FieldAndHeader {
private final Rule.Condition.Field field;
private final String headerName;
public FieldAndHeader(Rule.Condition.Field field, String headerName) {
this.field = field;
this.headerName = headerName;
}
}
@FunctionalInterface
interface AttributeEquals {
void isEqualTo(Attribute other);
}
public static AttributeEquals assertThatAttribute(Attribute attribute) {
return other -> {
assertThat(attribute.getName()).isEqualTo(other.getName());
assertThat(unbox(attribute)).isEqualTo(unbox(other));
};
}
public static AttributeEquals assertThatAttribute(Optional<Attribute> attribute) {
return assertThatAttribute(attribute.get());
}
static Pair<AttributeName, String> unbox(Attribute attribute) {
Collection<AttributeValue> collection = (Collection<AttributeValue>) attribute.getValue().getValue();
return Pair.of(attribute.getName(), (String) collection.stream().findFirst().get().getValue());
}
public static final ImmutableList<FieldAndHeader> ADDRESS_TESTING_COMBINATION = ImmutableList.of(
new FieldAndHeader(Field.FROM, RFC2822Headers.FROM),
new FieldAndHeader(Field.TO, RFC2822Headers.TO),
new FieldAndHeader(Field.CC, RFC2822Headers.CC),
new FieldAndHeader(Field.RECIPIENT, RFC2822Headers.TO),
new FieldAndHeader(Field.RECIPIENT, RFC2822Headers.CC));
static Stream<Arguments> exactlyEqualsTestSuite() {
return StreamUtils.flatten(
ADDRESS_TESTING_COMBINATION
.stream()
.flatMap(fieldAndHeader -> Stream.of(
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch(USER_1_USERNAME),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch(USER_1_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a full " + fieldAndHeader.headerName + " header with multiple addresses")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", " + USER_2_FULL_ADDRESS)
.valueToMatch(USER_1_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a failing " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "invalid@ white.space.in.domain.tld")
.valueToMatch("invalid@ white.space.in.domain.tld"),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a coma quoted " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>")
.valueToMatch("\"a, b\"@quoted.com"),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a coma quoted " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>")
.valueToMatch("Toto"),
argumentBuilder(fieldAndHeader.field)
.description("Full exact match in a coma quoted " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>")
.valueToMatch("Toto <\"a, b\"@quoted.com>"),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a failing + coma quoted" + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>")
.valueToMatch("\"a, b\"@quoted.com"),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a failing + coma quoted " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>")
.valueToMatch("Toto"),
argumentBuilder(fieldAndHeader.field)
.description("Full exact match in a failing + coma quoted " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>")
.valueToMatch("Toto <\"a, b\"@quoted.com>"),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld")
.valueToMatch(USER_1_FULL_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld")
.valueToMatch(USER_1_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld")
.valueToMatch(USER_1_USERNAME),
argumentBuilder(fieldAndHeader.field)
.description("Full header exact match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch(USER_1_FULL_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Exact match in a full " + fieldAndHeader.headerName + " header with a symetric emailer")
.header(fieldAndHeader.headerName, "\"toto@domain.tld\" <toto@domain.tld>")
.valueToMatch("toto@domain.tld"),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a username only " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_USERNAME)
.valueToMatch(USER_1_USERNAME),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in an address only " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_ADDRESS)
.valueToMatch(USER_1_ADDRESS),
argumentBuilder(fieldAndHeader.field)
.description("Username matching in multiple " + fieldAndHeader.headerName + " headers")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.header(fieldAndHeader.headerName, USER_2_FULL_ADDRESS)
.valueToMatch(USER_1_USERNAME),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a scrambled full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
.valueToMatch(FRED_MARTIN_FULLNAME),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a folded full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
.valueToMatch(UNFOLDED_USERNAME),
argumentBuilder(fieldAndHeader.field)
.description("Username exact match in a full " + fieldAndHeader.headerName + " with an invalid address")
.header(fieldAndHeader.headerName, "Benoit <invalid>")
.valueToMatch("Benoit"),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a full " + fieldAndHeader.headerName + " with an invalid address")
.header(fieldAndHeader.headerName, "Benoit <invalid>")
.valueToMatch("invalid"),
argumentBuilder(fieldAndHeader.field)
.description("Full header exact match in a full " + fieldAndHeader.headerName + " with an invalid address")
.header(fieldAndHeader.headerName, "Benoit <invalid>")
.valueToMatch("Benoit <invalid>"),
argumentBuilder(fieldAndHeader.field)
.description("Full header exact match in a full " + fieldAndHeader.headerName + " with an invalid structure")
.header(fieldAndHeader.headerName, "Benoit <invalid")
.valueToMatch("Benoit <invalid"),
argumentBuilder(fieldAndHeader.field)
.description("Full header exact match in a full " + fieldAndHeader.headerName + " with an invalid structure - multi address")
.header(fieldAndHeader.headerName, "Valid <toto@domain.tld>, Benoit <invalid")
.valueToMatch("Benoit <invalid"))
.flatMap(JMAPFilteringTest::forBothCase)),
Stream.of(
argumentBuilder().description("Full header match with multiple to and cc headers")
.field(RECIPIENT)
.ccRecipient(USER_1_FULL_ADDRESS)
.ccRecipient(USER_2_FULL_ADDRESS)
.toRecipient(USER_3_FULL_ADDRESS)
.toRecipient(USER_4_FULL_ADDRESS)
.valueToMatch(USER_4_FULL_ADDRESS)
.build(),
argumentBuilder().scrambledSubjectToMatch(UNSCRAMBLED_SUBJECT).build(),
argumentBuilder().unscrambledSubjectToMatch(UNSCRAMBLED_SUBJECT).build()));
}
static Stream<Arguments> containsTestSuite() {
return Stream.concat(
exactlyEqualsTestSuite(),
containsArguments());
}
private static Stream<Arguments> containsArguments() {
return StreamUtils.flatten(
ADDRESS_TESTING_COMBINATION.stream()
.flatMap(fieldAndHeader -> Stream.of(
argumentBuilder(fieldAndHeader.field)
.description("Full header partial match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch("ser1 <"),
argumentBuilder(fieldAndHeader.field)
.description("Address exact match in a full " + fieldAndHeader.headerName + " header with multiple addresses")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", Invalid <invalid@ white.space.in.domain.tld>")
.valueToMatch("invalid@ white.space.in.domain.tld"),
argumentBuilder(fieldAndHeader.field)
.description("Address partial match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch("ser1@jam"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a full " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName,USER_1_FULL_ADDRESS)
.valueToMatch("ser1"),
argumentBuilder(fieldAndHeader.field)
.description("Address partial match in an address only " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName,USER_1_ADDRESS)
.valueToMatch("ser1@jam"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a headername only " + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName,GA_BOU_ZO_MEU_FULL_ADDRESS)
.valueToMatch(BOU),
argumentBuilder(fieldAndHeader.field)
.description("Address partial match against multiple" + fieldAndHeader.headerName + " header")
.header(fieldAndHeader.headerName,USER_1_FULL_ADDRESS)
.header(fieldAndHeader.headerName,USER_2_FULL_ADDRESS)
.valueToMatch("ser1@jam"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a scrambled " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName,FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
.valueToMatch("déric MAR"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a folded " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName,USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
.valueToMatch("ded_us"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a " + fieldAndHeader.headerName + " full header with invalid address")
.header(fieldAndHeader.headerName,"Benoit <invalid>")
.valueToMatch("enoi"),
argumentBuilder(fieldAndHeader.field)
.description("Address partial match in a " + fieldAndHeader.headerName + " full header with invalid address")
.header(fieldAndHeader.headerName,"Benoit <invalid>")
.valueToMatch("nvali"),
argumentBuilder(fieldAndHeader.field)
.description("Full header partial match in a " + fieldAndHeader.headerName + " full header with invalid address")
.header(fieldAndHeader.headerName,"Benoit <invalid>")
.valueToMatch("enoit <invali"),
argumentBuilder(fieldAndHeader.field)
.description("Full header partial match in a " + fieldAndHeader.headerName + " full header with invalid structure")
.header(fieldAndHeader.headerName,"Benoit <invalid")
.valueToMatch("enoit <invali"),
argumentBuilder(fieldAndHeader.field)
.description("Username partial match in a " + fieldAndHeader.headerName + " full header with invalid structure")
.header(fieldAndHeader.headerName,"Benoit <invalid")
.valueToMatch("enoi"),
argumentBuilder(fieldAndHeader.field)
.description("Address partial match in a " + fieldAndHeader.headerName + " full header with invalid structure")
.header(fieldAndHeader.headerName,"Benoit <invalid")
.valueToMatch("nvali"))
.flatMap(JMAPFilteringTest::forBothCase)),
Stream.of(
argumentBuilder().description("multiple to and cc headers (partial matching)").field(RECIPIENT)
.ccRecipient(USER_1_FULL_ADDRESS)
.ccRecipient(USER_2_FULL_ADDRESS)
.toRecipient(USER_3_FULL_ADDRESS)
.toRecipient(USER_4_FULL_ADDRESS)
.valueToMatch("user4@jam").build(),
argumentBuilder().scrambledSubjectToMatch("is the subject").build(),
argumentBuilder().unscrambledSubjectToMatch("rédéric MART").build()));
}
static Stream<Arguments> notEqualsTestSuite() {
return Stream.concat(
notContainsTestSuite(),
containsArguments());
}
static Stream<Arguments> notContainsTestSuite() {
return StreamUtils.flatten(
ADDRESS_TESTING_COMBINATION.stream()
.flatMap(fieldAndHeader -> Stream.of(
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in a " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in multiple " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
.header(fieldAndHeader.headerName, USER_2_FULL_ADDRESS)
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in a scrambled " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in a folded " + fieldAndHeader.headerName + " full header")
.header(fieldAndHeader.headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in a " + fieldAndHeader.headerName + " empty header")
.header(fieldAndHeader.headerName, EMPTY)
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch when different address in a fully specified emailer for " + fieldAndHeader.headerName + " field")
.header(fieldAndHeader.headerName, "\"me\" <notme@example.com>")
.valueToMatch("\"me\" <me@example.com>"),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch when different username in a fully specified emailer for " + fieldAndHeader.headerName + " field")
.header(fieldAndHeader.headerName, "\"notme\" <me@example.com>")
.valueToMatch("\"definitlyme\" <me@example.com>"),
argumentBuilder(fieldAndHeader.field)
.description("No match in a full " + fieldAndHeader.headerName + " header with a symetric emailer - different personal")
.header(fieldAndHeader.headerName, "\"toto@domain.tld\" <toto@domain.tld>")
.valueToMatch("\"tata@domain.tld\" <toto@domain.tld>"),
argumentBuilder(fieldAndHeader.field)
.description("No match in a full " + fieldAndHeader.headerName + " header with a symetric emailer - different address")
.header(fieldAndHeader.headerName, "\"toto@domain.tld\" <toto@domain.tld>")
.valueToMatch("\"toto@domain.tld\" <tata@domain.tld>"),
argumentBuilder(fieldAndHeader.field)
.description("Nomatch in a missing " + fieldAndHeader.headerName + " header")
.valueToMatch(SHOULD_NOT_MATCH),
argumentBuilder(fieldAndHeader.field)
.description("No username match in a " + fieldAndHeader.headerName + " full header with invalid structure")
.header(fieldAndHeader.headerName, "Benoit <invalid>")
.valueToMatch(SHOULD_NOT_MATCH))
.map(FilteringArgumentBuilder::build)),
Stream.of(
argumentBuilder().description("multiple to and cc headers")
.field(RECIPIENT)
.ccRecipient(USER_1_FULL_ADDRESS)
.ccRecipient(USER_2_FULL_ADDRESS)
.toRecipient(USER_3_FULL_ADDRESS)
.toRecipient(USER_4_FULL_ADDRESS)
.valueToMatch(SHOULD_NOT_MATCH)
.build(),
argumentBuilder().description("not matching bcc headers")
.field(RECIPIENT)
.bccRecipient(USER_1_FULL_ADDRESS)
.valueToMatch(USER_1_FULL_ADDRESS)
.build(),
argumentBuilder().scrambledSubjectToMatch(SHOULD_NOT_MATCH).build(),
argumentBuilder().scrambledSubjectShouldNotMatchCaseSensitive().build(),
argumentBuilder().unscrambledSubjectToMatch(SHOULD_NOT_MATCH).build(),
argumentBuilder().unscrambledSubjectShouldNotMatchCaseSensitive().build()),
Stream.of(Rule.Condition.Field.values())
.map(field -> argumentBuilder()
.description("no header")
.field(field)
.noHeader()
.valueToMatch(USER_1_USERNAME)
.build()));
}
@ParameterizedTest(name = "CONTAINS should match for field {1}: {0}")
@MethodSource("containsTestSuite")
void matchingContainsTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, CONTAINS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@ParameterizedTest(name = "CONTAINS should not match for field {1}: {0}")
@MethodSource("notContainsTestSuite")
void notMatchingContainsTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, CONTAINS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@ParameterizedTest(name = "NOT-CONTAINS should match for field {1}: {0}")
@MethodSource("notContainsTestSuite")
void matchingNotContainsTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_CONTAINS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@ParameterizedTest(name = "NOT-CONTAINS should not match for field {1}: {0}")
@MethodSource("containsTestSuite")
void notContainsNotMatchingTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_CONTAINS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@ParameterizedTest(name = "EXACTLY-EQUALS should match for field {1}: {0}")
@MethodSource("exactlyEqualsTestSuite")
void equalsMatchingTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, EXACTLY_EQUALS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@ParameterizedTest(name = "EXACTLY-EQUALS should not match for field {1}: {0}")
@MethodSource("notEqualsTestSuite")
void equalsNotMatchingTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, EXACTLY_EQUALS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@ParameterizedTest(name = "NOT_EXACTLY_EQUALS should match for field {1}: {0}")
@MethodSource("notEqualsTestSuite")
void notEqualsMatchingTest(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_EXACTLY_EQUALS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@ParameterizedTest(name = "NOT_EXACTLY_EQUALS should not match for field {1}: {0}")
@MethodSource("exactlyEqualsTestSuite")
void notMatchingNotEqualsTests(String testDescription,
Rule.Condition.Field fieldToMatch,
MimeMessageBuilder mimeMessageBuilder,
String valueToMatch,
JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_EXACTLY_EQUALS, valueToMatch));
FakeMail mail = testSystem.asMail(mimeMessageBuilder);
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@Nested
class MultiRuleBehaviourTest {
@Test
void mailDirectiveShouldSetLastMatchedRuleWhenMultipleRules(JMAPFilteringTestSystem testSystem) throws Exception {
MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
MailboxId mailbox2Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
MailboxId mailbox3Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox1Id.serialize())))
.build(),
Rule.builder()
.id(Rule.Id.of("2"))
.name("rule 2")
.conditionGroup(Rule.Condition.of(FROM, NOT_CONTAINS, USER_1_USERNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox2Id.serialize())))
.build(),
Rule.builder()
.id(Rule.Id.of("3"))
.name("rule 3")
.conditionGroup(Rule.Condition.of(TO, EXACTLY_EQUALS, USER_3_ADDRESS))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox3Id.serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(USER_2_ADDRESS)
.addToRecipient(USER_3_ADDRESS)
.setSubject(UNSCRAMBLED_SUBJECT));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(new Attribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME, AttributeValue.of(ImmutableList.of(AttributeValue.of("RECIPIENT_1_MAILBOX_3")))));
}
@Test
void mailDirectiveShouldSetLastMatchedMailboxWhenMultipleMailboxes(JMAPFilteringTestSystem testSystem) throws Exception {
MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
MailboxId mailbox2Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
MailboxId mailbox3Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
mailbox3Id.serialize(),
mailbox2Id.serialize(),
mailbox1Id.serialize()))))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.setSubject(UNSCRAMBLED_SUBJECT));
testSystem.getJmapFiltering().service(mail);
assertThat((ImmutableSet) mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME).get().getValue().value())
.containsOnly(AttributeValue.of("RECIPIENT_1_MAILBOX_3"),
AttributeValue.of("RECIPIENT_1_MAILBOX_2"),
AttributeValue.of("RECIPIENT_1_MAILBOX_1"));
}
@Test
void rulesWithEmptyMailboxIdsShouldBeSkept(JMAPFilteringTestSystem testSystem) throws Exception {
MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of())))
.build(),
Rule.builder()
.id(Rule.Id.of("2"))
.name("rule 2")
.conditionGroup(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
mailbox1Id.serialize()))))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.setSubject(UNSCRAMBLED_SUBJECT));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(new Attribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME, AttributeValue.of(ImmutableList.of(AttributeValue.of("RECIPIENT_1_MAILBOX_1")))));
}
@Test
void mailDirectiveShouldNotBeSetWhenAllDoNotExactlyEqualsRuleValue(JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(
Rule.Condition.of(FROM, CONTAINS, USER_1_FULL_ADDRESS),
Rule.Condition.of(FROM, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
Rule.Condition.of(TO, CONTAINS, USER_1_FULL_ADDRESS),
Rule.Condition.of(TO, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
Rule.Condition.of(CC, CONTAINS, USER_1_FULL_ADDRESS),
Rule.Condition.of(CC, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
Rule.Condition.of(SUBJECT, CONTAINS, USER_1_FULL_ADDRESS),
Rule.Condition.of(SUBJECT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS));
FakeMail mail = testSystem.asMail(mimeMessageBuilder());
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@Test
void mailDirectiveShouldNotBeSetWhenNoneRulesValueIsContained(JMAPFilteringTestSystem testSystem) throws Exception {
testSystem.defineRulesForRecipient1(
Rule.Condition.of(FROM, CONTAINS, SHOULD_NOT_MATCH),
Rule.Condition.of(TO, CONTAINS, SHOULD_NOT_MATCH),
Rule.Condition.of(CC, CONTAINS, SHOULD_NOT_MATCH));
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(USER_1_FULL_ADDRESS)
.addToRecipient(USER_2_FULL_ADDRESS)
.addCcRecipient(USER_3_FULL_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
}
@Nested
class UnknownMailboxIds {
@Test
void serviceShouldNotThrowWhenUnknownMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
String unknownMailboxId = "4242";
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
assertThatCode(() -> testSystem.getJmapFiltering().service(mail))
.doesNotThrowAnyException();
}
@Test
void mailDirectiveShouldNotBeSetWhenUnknownMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
String unknownMailboxId = "4242";
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEmpty();
}
@Test
void rulesWithInvalidMailboxIdsShouldBeSkept(JMAPFilteringTestSystem testSystem) throws Exception {
String unknownMailboxId = "4242";
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
.build(),
Rule.builder()
.id(Rule.Id.of("2"))
.name("rule 2")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(
testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@Test
void rulesWithMultipleMailboxIdsShouldFallbackWhenInvalidFirstMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
String unknownMailboxId = "4242";
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(
unknownMailboxId,
testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
}
@Test
void actionShouldSupportReject(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(),
false, false, true, ImmutableList.of()))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getRecipients()).isEmpty();
}
@Test
void actionShouldSupportSeen(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(),
true, false, false, ImmutableList.of()))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(StorageDirective.fromMail(Username.of("recipient1"), mail))
.isEqualTo(StorageDirective.builder()
.seen(Optional.of(true))
.build());
}
@Test
void actionShouldSupportImportant(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(),
false, true, false, ImmutableList.of()))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(StorageDirective.fromMail(Username.of("recipient1"), mail))
.isEqualTo(StorageDirective.builder()
.important(Optional.of(true))
.build());
}
@Test
void actionShouldSupportKeywords(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(),
false, false, false, ImmutableList.of("abc", "def")))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
assertThat(StorageDirective.fromMail(Username.of("recipient1"), mail).getFlags().get().getUserFlags())
.containsOnly("abc", "def");
}
@Test
void actionShouldCombineFlags(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(),
true, true, false, ImmutableList.of("abc", "def")))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder()
.addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
testSystem.getJmapFiltering().service(mail);
Flags expectedFlags = new Flags();
expectedFlags.add("abc");
expectedFlags.add("def");
expectedFlags.add(Flags.Flag.SEEN);
expectedFlags.add(Flags.Flag.FLAGGED);
assertThat(StorageDirective.fromMail(Username.of("recipient1"), mail).getFlags().get())
.isEqualTo(expectedFlags);
}
@Test
void andShouldMatchWhenAllConditionsAreMet(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.ConditionGroup.of(Rule.ConditionCombiner.AND, Rule.Condition.of(FROM, CONTAINS, USER_2_USERNAME), Rule.Condition.of(SUBJECT, CONTAINS, "cd")))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder().addHeader(FROM.asString(), USER_2_ADDRESS).addHeader(SUBJECT.asString(), "abcdef"));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@Test
void andShouldNotMatchWhenSomeConditionsAreNotMet(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.ConditionGroup.of(Rule.ConditionCombiner.AND, Rule.Condition.of(FROM, CONTAINS, USER_2_USERNAME), Rule.Condition.of(SUBJECT, CONTAINS, "cdf")))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder().addHeader(FROM.asString(), USER_2_ADDRESS).addHeader(SUBJECT.asString(), "abcdef"));
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME).isEmpty()).isTrue();
}
@Test
void orShouldMatchWhenSomeConditionsAreMet(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.ConditionGroup.of(Rule.ConditionCombiner.OR, Rule.Condition.of(FROM, CONTAINS, USER_2_USERNAME), Rule.Condition.of(SUBJECT, CONTAINS, "cdf")))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder().addHeader(FROM.asString(), USER_2_ADDRESS).addHeader(SUBJECT.asString(), "abcdef"));
testSystem.getJmapFiltering().service(mail);
assertThatAttribute(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME))
.isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE);
}
@Test
void orShouldNotMatchWhenNoConditionsAreMet(JMAPFilteringTestSystem testSystem) throws Exception {
Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME,
Optional.empty(),
Rule.builder()
.id(Rule.Id.of("1"))
.name("rule 1")
.conditionGroup(Rule.ConditionGroup.of(Rule.ConditionCombiner.OR, Rule.Condition.of(FROM, CONTAINS, USER_2_USERNAME), Rule.Condition.of(SUBJECT, CONTAINS, "cdf")))
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize())))
.build())).block();
FakeMail mail = testSystem.asMail(mimeMessageBuilder().addHeader(FROM.asString(), USER_3_ADDRESS).addHeader(SUBJECT.asString(), "abcdef"));
testSystem.getJmapFiltering().service(mail);
assertThat(mail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME).isEmpty()).isTrue();
}
}