Repository: DependencyTrack/dependency-track (java)

Issue:
Team recepients don't work for scheduled notifications published via Email
### Current Behavior

As identified by @rkg-mm, scheduled notifications are not sent via email, if teams are configured as recipients. 

It turns out that by the time DT tries to collect email addresses from team members, members are not loaded:

![Image](https://github.com/user-attachments/assets/01df3b08-1cd8-4f98-9e1a-5eae43f6ec61)

### Steps to Reproduce

N/A

### Expected Behavior

Scheduled notifications can be delivered to members of teams associated with the respective notification rule.

### Dependency-Track Version

4.13.0

### Dependency-Track Distribution

Container Image

### Database Server

N/A

### Database Server Version

_No response_

### Browser

N/A

### Checklist

- [x] I have read and understand the [contributing guidelines](https://github.com/DependencyTrack/dependency-track/blob/master/CONTRIBUTING.md#filing-issues)
- [x] I have checked the [existing issues](https://github.com/DependencyTrack/dependency-track/issues) for whether this defect was already reported

Pull request description:
Fix team email addresses not being available when publishing scheduled notification emails
### Description

<!-- REQUIRED
    Provide a concise description of your change. What does it do? Why is it necessary?
    As a guideline, think about how you would describe your change if you were to write a changelog entry for it.
-->

Fixes team email addresses not being available when publishing scheduled notification emails.

**TODO**:

- [x] Test with H2
- [x] Test with MSSQL
- [x] Test with PostgreSQL

### Addressed Issue

<!-- REQUIRED
    Reference the issue addressed by this PR, e.g. `#1234`.
    Use keywords like `closes` or `fixes` to signal that this PR resolves the issue,
    causing the issue to be closed automatically when the PR is merged:
        https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->

Fixes #4840

### Additional Details

<!-- OPTIONAL
    If desired, share more technical details about the change here.
    Elaborating on why you implemented the change the way you did can be super helpful to the reviewer.
    Did you consider other solutions? Any problems you ran into along the way?
-->

N/A

### Checklist

<!-- REQUIRED
    Mark items in this list as done by adding a `x` between the square brackets.
    Non-applicable items may be marked as such by surrounding their text with tildes (`~`).

    This is not meant to be a strict to-do list. If you're unsure about anything,
    just leave it empty for now. The maintainers are happy to assist you in figuring it out!
-->

- [x] I have read and understand the [contributing guidelines](../CONTRIBUTING.md#pull-requests)
- [x] This PR fixes a defect, and I have provided tests to verify that the fix is effective
- ~This PR implements an enhancement, and I have provided tests to verify that it works as intended~
- ~This PR introduces changes to the database model, and I have added corresponding [update logic](https://github.com/DependencyTrack/dependency-track/tree/master/src/main/java/org/dependencytrack/upgrade)~
- ~This PR introduces new or alters existing behavior, and I have updated the [documentation](https://github.com/DependencyTrack/dependency-track/tree/master/docs/_docs) accordingly~


Patch:
diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml
index 7136265de..28b9d5c19 100644
--- a/.github/workflows/_meta-build.yaml
+++ b/.github/workflows/_meta-build.yaml
@@ -31,7 +31,7 @@ jobs:
         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
 
       - name: Set up JDK
-        uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v4.7.1
+        uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # tag=v4.7.0
         with:
           distribution: 'temurin'
           java-version: '21'
@@ -149,6 +149,6 @@ jobs:
 
       - name: Upload Trivy Scan Results to GitHub Security Tab
         if: ${{ inputs.publish-container }}
-        uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # tag=v3.28.15
+        uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # tag=v3.28.13
         with:
           sarif_file: 'trivy-results.sarif'
diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml
index 1a30e97cf..966a80b92 100644
--- a/.github/workflows/ci-release.yaml
+++ b/.github/workflows/ci-release.yaml
@@ -54,7 +54,7 @@ jobs:
         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
 
       - name: Set up JDK
-        uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v4.7.1
+        uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # tag=v4.7.0
         with:
           distribution: 'temurin'
           java-version: '21'
diff --git a/pom.xml b/pom.xml
index bef12af5b..f034f07aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,7 +95,7 @@
         <lib.cloud-sql-postgres-socket-factory.version>1.24.1</lib.cloud-sql-postgres-socket-factory.version>
         <lib.cpe-parser.version>3.0.0</lib.cpe-parser.version>
         <lib.commons-compress.version>1.27.1</lib.commons-compress.version>
-        <lib.commons-text.version>1.13.1</lib.commons-text.version>
+        <lib.commons-text.version>1.13.0</lib.commons-text.version>
         <lib.cvss-calculator.version>1.4.2</lib.cvss-calculator.version>
         <lib.owasp-rr-calculator.version>1.0.1</lib.owasp-rr-calculator.version>
         <lib.cyclonedx-java.version>10.2.1</lib.cyclonedx-java.version>
diff --git a/src/main/java/org/dependencytrack/notification/NotificationRouter.java b/src/main/java/org/dependencytrack/notification/NotificationRouter.java
index 5a1ad731a..edfecf84d 100644
--- a/src/main/java/org/dependencytrack/notification/NotificationRouter.java
+++ b/src/main/java/org/dependencytrack/notification/NotificationRouter.java
@@ -29,7 +29,6 @@ import org.dependencytrack.model.Project;
 import org.dependencytrack.model.Tag;
 import org.dependencytrack.notification.publisher.PublishContext;
 import org.dependencytrack.notification.publisher.Publisher;
-import org.dependencytrack.notification.publisher.SendMailPublisher;
 import org.dependencytrack.notification.vo.AnalysisDecisionChange;
 import org.dependencytrack.notification.vo.BomConsumedOrProcessed;
 import org.dependencytrack.notification.vo.BomProcessingFailed;
@@ -91,11 +90,7 @@ public class NotificationRouter implements Subscriber {
                             .add(CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate())
                             .addAll(Json.createObjectBuilder(config))
                             .build();
-                    if (publisherClass != SendMailPublisher.class || rule.getTeams().isEmpty() || rule.getTeams() == null) {
-                        publisher.inform(ruleCtx, restrictNotificationToRuleProjects(notification, rule), notificationPublisherConfig);
-                    } else {
-                        ((SendMailPublisher) publisher).inform(ruleCtx, restrictNotificationToRuleProjects(notification, rule), notificationPublisherConfig, rule.getTeams());
-                    }
+                    publisher.inform(ruleCtx, restrictNotificationToRuleProjects(notification, rule), notificationPublisherConfig);
                 } else {
                     LOGGER.error("The defined notification publisher is not assignable from " + Publisher.class.getCanonicalName() + " (%s)".formatted(ruleCtx));
                 }
diff --git a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java
index 36f377d57..27b00438a 100644
--- a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java
+++ b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java
@@ -47,15 +47,24 @@ import java.util.UUID;
  * @param notificationScope     Scope of the {@link Notification} being published
  * @param notificationTimestamp UTC Timestamp in {@link DateTimeFormatter#ISO_DATE_TIME} of the {@link Notification} being published
  * @param notificationSubjects  Subject(s) of the {@link Notification} being published
+ * @param ruleId                ID of the matched {@link NotificationRule}
  * @param ruleName              Name of the matched {@link NotificationRule}
  * @param ruleScope             Scope of the matched {@link NotificationRule}
  * @param ruleLevel             Level of the matched {@link NotificationRule}
  * @param logSuccess            Whether the publisher shall emit a log message upon successful publishing
  * @since 4.10.0
  */
-public record PublishContext(String notificationGroup, String notificationLevel, String notificationScope,
-                             String notificationTimestamp, Map<String, Object> notificationSubjects,
-                             String ruleName, String ruleScope, String ruleLevel, Boolean logSuccess) {
+public record PublishContext(
+        String notificationGroup,
+        String notificationLevel,
+        String notificationScope,
+        String notificationTimestamp,
+        Map<String, Object> notificationSubjects,
+        long ruleId,
+        String ruleName,
+        String ruleScope,
+        String ruleLevel,
+        Boolean logSuccess) {
 
     private static final String SUBJECT_COMPONENT = "component";
     private static final String SUBJECT_PROJECT = "project";
@@ -109,9 +118,17 @@ public record PublishContext(String notificationGroup, String notificationLevel,
             notificationSubjects.put(SUBJECT_PROJECTS, subject.summary().projectSummaries().keySet().stream().map(Project::convert).toList());
         }
 
-        return new PublishContext(notification.getGroup(), Optional.ofNullable(notification.getLevel()).map(Enum::name).orElse(null),
-                notification.getScope(), notification.getTimestamp().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_DATE_TIME), notificationSubjects,
-                /* ruleName */ null, /* ruleScope */ null, /* ruleLevel */ null, /* logSuccess */ null);
+        return new PublishContext(
+                notification.getGroup(),
+                Optional.ofNullable(notification.getLevel()).map(Enum::name).orElse(null),
+                notification.getScope(),
+                notification.getTimestamp().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_DATE_TIME),
+                notificationSubjects,
+                /* ruleId */ -1,
+                /* ruleName */ null,
+                /* ruleScope */ null,
+                /* ruleLevel */ null,
+                /* logSuccess */ null);
     }
 
     /**
@@ -121,8 +138,17 @@ public record PublishContext(String notificationGroup, String notificationLevel,
      * @return This {@link PublishContext}
      */
     public PublishContext withRule(final NotificationRule rule) {
-        return new PublishContext(this.notificationGroup, this.notificationLevel, this.notificationScope, this.notificationTimestamp,
-                this.notificationSubjects, rule.getName(), rule.getScope().name(), rule.getNotificationLevel().name(), rule.isLogSuccessfulPublish());
+        return new PublishContext(
+                this.notificationGroup,
+                this.notificationLevel,
+                this.notificationScope,
+                this.notificationTimestamp,
+                this.notificationSubjects,
+                rule.getId(),
+                rule.getName(),
+                rule.getScope().name(),
+                rule.getNotificationLevel().name(),
+                rule.isLogSuccessfulPublish());
     }
 
     public boolean shouldLogSuccess() {
diff --git a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java
index a1df791d2..07df5bb89 100644
--- a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java
+++ b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java
@@ -19,10 +19,6 @@
 package org.dependencytrack.notification.publisher;
 
 import alpine.common.logging.Logger;
-import alpine.model.LdapUser;
-import alpine.model.ManagedUser;
-import alpine.model.OidcUser;
-import alpine.model.Team;
 import alpine.notification.Notification;
 import alpine.server.mail.SendMail;
 import alpine.server.mail.SendMailException;
@@ -38,12 +34,10 @@ import jakarta.json.JsonString;
 import jakarta.ws.rs.core.MediaType;
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Predicate;
-import java.util.stream.Stream;
 
 import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_PREFIX;
 import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_ENABLED;
@@ -65,21 +59,13 @@ public class SendMailPublisher implements Publisher {
             .newLineTrimming(false)
             .build();
 
+    @Override
     public void inform(final PublishContext ctx, final Notification notification, final JsonObject config) {
         if (config == null) {
             LOGGER.warn("No configuration found; Skipping notification (%s)".formatted(ctx));
             return;
         }
-        final String[] destinations = parseDestination(config);
-        sendNotification(ctx, notification, config, destinations);
-    }
-
-    public void inform(final PublishContext ctx, final Notification notification, final JsonObject config, List<Team> teams) {
-        if (config == null) {
-            LOGGER.warn("No configuration found. Skipping notification. (%s)".formatted(ctx));
-            return;
-        }
-        final String[] destinations = parseDestination(config, teams);
+        final String[] destinations = getDestinations(config, ctx.ruleId());
         sendNotification(ctx, notification, config, destinations);
     }
 
@@ -150,7 +136,7 @@ public class SendMailPublisher implements Publisher {
                     .from(smtpFrom)
                     .to(destinations)
                     .subject(emailSubjectPrefix + " " + notification.getTitle())
-                    .body(mimeType == MediaType.TEXT_HTML ? StringEscapeUtils.escapeHtml4(unescapedContent): unescapedContent)
+                    .body(MediaType.TEXT_HTML.equals(mimeType) ? StringEscapeUtils.escapeHtml4(unescapedContent) : unescapedContent)
                     .bodyMimeType(mimeType)
                     .host(smtpHostname)
                     .port(smtpPort)
@@ -177,31 +163,20 @@ public class SendMailPublisher implements Publisher {
         return ENGINE;
     }
 
-    static String[] parseDestination(final JsonObject config) {
-        JsonString destinationString = config.getJsonString("destination");
-        if ((destinationString == null) || destinationString.getString().isEmpty()) {
-          return null;
+    static String[] getDestinations(final JsonObject config, final long ruleId) {
+        final var emails = new HashSet<String>();
+
+        Optional.ofNullable(config.getJsonString("destination"))
+                .map(JsonString::getString)
+                .stream()
+                .flatMap(dest -> Arrays.stream(dest.split(",")))
+                .filter(Predicate.not(String::isEmpty))
+                .forEach(emails::add);
+
+        try (final var qm = new QueryManager()) {
+            emails.addAll(qm.getTeamMemberEmailsForNotificationRule(ruleId));
         }
-        return destinationString.getString().split(",");
-    }
 
-    static String[] parseDestination(final JsonObject config, final List<Team> teams) {
-        String[] destination = teams.stream().flatMap(
-                team -> Stream.of(
-                                Optional.ofNullable(config.getJsonString("destination"))
-                                        .map(JsonString::getString)
-                                        .stream()
-                                        .flatMap(dest -> Arrays.stream(dest.split(",")))
-                                        .filter(Predicate.not(String::isEmpty)),
-                                Optional.ofNullable(team.getManagedUsers()).orElseGet(Collections::emptyList).stream().map(ManagedUser::getEmail).filter(Objects::nonNull),
-                                Optional.ofNullable(team.getLdapUsers()).orElseGet(Collections::emptyList).stream().map(LdapUser::getEmail).filter(Objects::nonNull),
-                                Optional.ofNullable(team.getOidcUsers()).orElseGet(Collections::emptyList).stream().map(OidcUser::getEmail).filter(Objects::nonNull)
-                        )
-                        .reduce(Stream::concat)
-                        .orElseGet(Stream::empty)
-                )
-                .distinct()
-                .toArray(String[]::new);
-        return destination.length == 0 ? null : destination;
+        return emails.isEmpty() ? null : emails.toArray(new String[0]);
     }
 }
diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java
index d67f2a206..4964241a6 100644
--- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java
+++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java
@@ -41,8 +41,10 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static org.dependencytrack.util.PersistenceUtil.assertPersistent;
 import static org.dependencytrack.util.PersistenceUtil.assertPersistentAll;
@@ -433,4 +435,48 @@ public class NotificationQueryManager extends QueryManager implements IQueryMana
         }
     }
 
+    /**
+     * @since 4.13.1
+     */
+    @Override
+    public Set<String> getTeamMemberEmailsForNotificationRule(final long ruleId) {
+        final Query<?> query = pm.newQuery(Query.SQL, /* language=SQL */ """
+                SELECT "MANAGEDUSER"."EMAIL" AS "EMAIL"
+                  FROM "NOTIFICATIONRULE_TEAMS"
+                 INNER JOIN "TEAM"
+                    ON "TEAM"."ID" = "NOTIFICATIONRULE_TEAMS"."TEAM_ID"
+                 INNER JOIN "MANAGEDUSERS_TEAMS"
+                    ON "MANAGEDUSERS_TEAMS"."TEAM_ID" = "TEAM"."ID"
+                 INNER JOIN "MANAGEDUSER"
+                    ON "MANAGEDUSER"."ID" = "MANAGEDUSERS_TEAMS"."MANAGEDUSER_ID"
+                 WHERE "NOTIFICATIONRULE_TEAMS"."NOTIFICATIONRULE_ID" = :ruleId
+                   AND "MANAGEDUSER"."EMAIL" IS NOT NULL
+                 UNION ALL
+                SELECT "LDAPUSER"."EMAIL" AS "EMAIL"
+                  FROM "NOTIFICATIONRULE_TEAMS"
+                 INNER JOIN "TEAM"
+                    ON "TEAM"."ID" = "NOTIFICATIONRULE_TEAMS"."TEAM_ID"
+                 INNER JOIN "LDAPUSERS_TEAMS"
+                    ON "LDAPUSERS_TEAMS"."TEAM_ID" = "TEAM"."ID"
+                 INNER JOIN "LDAPUSER"
+                    ON "LDAPUSER"."ID" = "LDAPUSERS_TEAMS"."LDAPUSER_ID"
+                 WHERE "NOTIFICATIONRULE_TEAMS"."NOTIFICATIONRULE_ID" = :ruleId
+                   AND "LDAPUSER"."EMAIL" IS NOT NULL
+                 UNION ALL
+                SELECT "OIDCUSER"."EMAIL" AS "EMAIL"
+                  FROM "NOTIFICATIONRULE_TEAMS"
+                 INNER JOIN "TEAM"
+                    ON "TEAM"."ID" = "NOTIFICATIONRULE_TEAMS"."TEAM_ID"
+                 INNER JOIN "OIDCUSERS_TEAMS"
+                    ON "OIDCUSERS_TEAMS"."TEAM_ID" = "TEAM"."ID"
+                 INNER JOIN "OIDCUSER"
+                    ON "OIDCUSER"."ID" = "OIDCUSERS_TEAMS"."OIDCUSERS_ID"
+                 WHERE "NOTIFICATIONRULE_TEAMS"."NOTIFICATIONRULE_ID" = :ruleId
+                   AND "OIDCUSER"."EMAIL" IS NOT NULL
+                """);
+        query.setNamedParameters(Map.of("ruleId", ruleId));
+
+        return new HashSet<>(executeAndCloseResultList(query, String.class));
+    }
+
 }
diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java
index de946cbf1..bd0fc756f 100644
--- a/src/main/java/org/dependencytrack/persistence/QueryManager.java
+++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java
@@ -1263,6 +1263,10 @@ public class QueryManager extends AlpineQueryManager {
         return getNotificationQueryManager().getVulnerabilitiesForNotificationById(ids);
     }
 
+    public Set<String> getTeamMemberEmailsForNotificationRule(final long ruleId) {
+        return getNotificationQueryManager().getTeamMemberEmailsForNotificationRule(ruleId);
+    }
+
     public List<NotificationPublisher> getAllNotificationPublishers() {
         return getNotificationQueryManager().getAllNotificationPublishers();
     }


Test patch:
diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml
index 4d0670636..a4025a8a9 100644
--- a/.github/workflows/ci-test.yaml
+++ b/.github/workflows/ci-test.yaml
@@ -36,7 +36,7 @@ jobs:
         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
 
       - name: Set up JDK
-        uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v4.7.1
+        uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # tag=v4.7.0
         with:
           distribution: 'temurin'
           java-version: '21'
diff --git a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java
index 8de2137a2..db45d6692 100644
--- a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java
+++ b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java
@@ -7,12 +7,14 @@ import alpine.model.Team;
 import alpine.notification.Notification;
 import alpine.notification.NotificationLevel;
 import alpine.security.crypto.DataEncryption;
+import alpine.server.auth.PasswordService;
 import com.icegreen.greenmail.junit4.GreenMailRule;
 import com.icegreen.greenmail.util.ServerSetup;
+import org.dependencytrack.model.NotificationPublisher;
+import org.dependencytrack.model.NotificationRule;
 import org.dependencytrack.notification.NotificationConstants;
 import org.dependencytrack.notification.NotificationGroup;
 import org.dependencytrack.notification.NotificationScope;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -42,6 +44,9 @@ import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_USERN
 
 public class SendMailPublisherTest extends AbstractPublisherTest<SendMailPublisher> {
 
+    // Hashing is expensive. Do it once and re-use across tests as much as possible.
+    protected static final String TEST_USER_PASSWORD_HASH = new String(PasswordService.createHash("testuser".toCharArray()));
+
     @Rule
     public final GreenMailRule greenMail = new GreenMailRule(ServerSetup.SMTP.dynamicPort())
             .withConfiguration(aConfig().
@@ -586,384 +591,368 @@ public class SendMailPublisherTest extends AbstractPublisherTest<SendMailPublish
     @Test
     public void testSingleDestination() {
         JsonObject config = configWithDestination("john@doe.com");
-        Assert.assertArrayEquals(new String[]{"john@doe.com"}, SendMailPublisher.parseDestination(config));
+        assertThat(SendMailPublisher.getDestinations(config, -1L)).containsOnly("john@doe.com");
     }
 
 
     @Test
     public void testMultipleDestinations() {
         JsonObject config = configWithDestination("john@doe.com,steve@jobs.org");
-        Assert.assertArrayEquals(new String[]{"john@doe.com", "steve@jobs.org"},
-                SendMailPublisher.parseDestination(config));
+        assertThat(SendMailPublisher.getDestinations(config, -1L))
+                .containsExactlyInAnyOrder("john@doe.com", "steve@jobs.org");
     }
 
     @Test
     public void testNullDestination() {
-        Assert.assertArrayEquals(null, SendMailPublisher.parseDestination(Json.createObjectBuilder().build()));
+        assertThat(SendMailPublisher.getDestinations(Json.createObjectBuilder().build(), -1L)).isNull();
     }
 
     @Test
     public void testEmptyDestinations() {
         JsonObject config = configWithDestination("");
-        Assert.assertArrayEquals(null, SendMailPublisher.parseDestination(config));
+        assertThat(SendMailPublisher.getDestinations(config, -1L)).isNull();
     }
 
     @Test
     public void testSingleTeamAsDestination() {
         JsonObject config = configWithDestination("");
 
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder("managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com");
     }
 
     @Test
     public void testMultipleTeamsAsDestination() {
         JsonObject config = configWithDestination("");
 
-        ManagedUser managedUser1 = new ManagedUser();
-        managedUser1.setUsername("ManagedUserTest");
+        ManagedUser managedUser1 = qm.createManagedUser("ManagedUserTest1", TEST_USER_PASSWORD_HASH);
         managedUser1.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers1 = new ArrayList<>();
-        managedUsers1.add(managedUser1);
 
-        LdapUser ldapUser1 = new LdapUser();
-        ldapUser1.setUsername("ldapUserTest");
+        LdapUser ldapUser1 = qm.createLdapUser("ldapUserTest1");
         ldapUser1.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers1 = new ArrayList<>();
-        ldapUsers1.add(ldapUser1);
 
-        OidcUser oidcUser1 = new OidcUser();
-        oidcUser1.setUsername("oidcUserTest");
+        OidcUser oidcUser1 = qm.createOidcUser("oidcUserTest1");
         oidcUser1.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers1 = new ArrayList<>();
-        oidcUsers1.add(oidcUser1);
 
         List<Team> teams = new ArrayList<>();
-        Team team1 = new Team();
-        team1.setManagedUsers(managedUsers1);
-        team1.setLdapUsers(ldapUsers1);
-        team1.setOidcUsers(oidcUsers1);
+        Team team1 = qm.createTeam("testTeam1");
+        qm.addUserToTeam(managedUser1, team1);
+        qm.addUserToTeam(ldapUser1, team1);
+        qm.addUserToTeam(oidcUser1, team1);
         teams.add(team1);
 
-        ManagedUser managedUser2 = new ManagedUser();
-        managedUser2.setUsername("ManagedUserTest");
+        ManagedUser managedUser2 = qm.createManagedUser("ManagedUserTest2", TEST_USER_PASSWORD_HASH);
         managedUser2.setEmail("anotherManagedUser@Test.com");
-        List<ManagedUser> managedUsers2 = new ArrayList<>();
-        managedUsers2.add(managedUser2);
 
-        LdapUser ldapUser2 = new LdapUser();
-        ldapUser2.setUsername("ldapUserTest");
+        LdapUser ldapUser2 = qm.createLdapUser("ldapUserTest2");
         ldapUser2.setEmail("anotherLdapUser@Test.com");
-        List<LdapUser> ldapUsers2 = new ArrayList<>();
-        ldapUsers2.add(ldapUser2);
 
-        OidcUser oidcUser2 = new OidcUser();
-        oidcUser2.setUsername("oidcUserTest");
+        OidcUser oidcUser2 = qm.createOidcUser("oidcUserTest2");
         oidcUser2.setEmail("anotherOidcUser@Test.com");
-        List<OidcUser> oidcUsers2 = new ArrayList<>();
-        oidcUsers2.add(oidcUser2);
 
-        Team team2 = new Team();
-        team2.setManagedUsers(managedUsers2);
-        team2.setLdapUsers(ldapUsers2);
-        team2.setOidcUsers(oidcUsers2);
+        Team team2 = qm.createTeam("testTeam2");
+        qm.addUserToTeam(managedUser2, team2);
+        qm.addUserToTeam(ldapUser2, team2);
+        qm.addUserToTeam(oidcUser2, team2);
         teams.add(team2);
 
-        Assert.assertArrayEquals(new String[]{"managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com", "anotherManagedUser@Test.com",
-                "anotherLdapUser@Test.com", "anotherOidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "anotherLdapUser@Test.com",
+                        "anotherManagedUser@Test.com",
+                        "anotherOidcUser@Test.com",
+                        "ldapUser@Test.com",
+                        "managedUser@Test.com",
+                        "oidcUser@Test.com");
     }
 
     @Test
     public void testDuplicateTeamAsDestination() {
         JsonObject config = configWithDestination("");
 
-        ManagedUser managedUser1 = new ManagedUser();
-        managedUser1.setUsername("ManagedUserTest");
+        ManagedUser managedUser1 = qm.createManagedUser("ManagedUserTest1", TEST_USER_PASSWORD_HASH);
         managedUser1.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers1 = new ArrayList<>();
-        managedUsers1.add(managedUser1);
 
-        LdapUser ldapUser1 = new LdapUser();
-        ldapUser1.setUsername("ldapUserTest");
+        LdapUser ldapUser1 = qm.createLdapUser("ldapUserTest1");
         ldapUser1.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers1 = new ArrayList<>();
-        ldapUsers1.add(ldapUser1);
 
-        OidcUser oidcUser1 = new OidcUser();
-        oidcUser1.setUsername("oidcUserTest");
+        OidcUser oidcUser1 = qm.createOidcUser("oidcUserTest1");
         oidcUser1.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers1 = new ArrayList<>();
-        oidcUsers1.add(oidcUser1);
 
         List<Team> teams = new ArrayList<>();
-        Team team1 = new Team();
-        team1.setManagedUsers(managedUsers1);
-        team1.setLdapUsers(ldapUsers1);
-        team1.setOidcUsers(oidcUsers1);
+        Team team1 = qm.createTeam("testTeam1");
+        qm.addUserToTeam(managedUser1, team1);
+        qm.addUserToTeam(ldapUser1, team1);
+        qm.addUserToTeam(oidcUser1, team1);
         teams.add(team1);
 
-        ManagedUser managedUser2 = new ManagedUser();
-        managedUser2.setUsername("ManagedUserTest");
+        ManagedUser managedUser2 = qm.createManagedUser("ManagedUserTest2", TEST_USER_PASSWORD_HASH);
         managedUser2.setEmail("anotherManagedUser@Test.com");
-        List<ManagedUser> managedUsers2 = new ArrayList<>();
-        managedUsers2.add(managedUser2);
 
-        LdapUser ldapUser2 = new LdapUser();
-        ldapUser2.setUsername("ldapUserTest");
+        LdapUser ldapUser2 = qm.createLdapUser("ldapUserTest2");
         ldapUser2.setEmail("anotherLdapUser@Test.com");
-        List<LdapUser> ldapUsers2 = new ArrayList<>();
-        ldapUsers2.add(ldapUser2);
 
-        OidcUser oidcUser2 = new OidcUser();
-        oidcUser2.setUsername("oidcUserTest");
+        OidcUser oidcUser2 = qm.createOidcUser("oidcUserTest2");
         oidcUser2.setEmail("anotherOidcUser@Test.com");
-        List<OidcUser> oidcUsers2 = new ArrayList<>();
-        oidcUsers2.add(oidcUser2);
-        oidcUsers2.add(oidcUser1);
-
-        Team team2 = new Team();
-        team2.setManagedUsers(managedUsers2);
-        team2.setLdapUsers(ldapUsers2);
-        team2.setOidcUsers(oidcUsers2);
+
+        Team team2 = qm.createTeam("testTeam2");
+        qm.addUserToTeam(managedUser2, team2);
+        qm.addUserToTeam(ldapUser2, team2);
+        qm.addUserToTeam(oidcUser2, team2);
+        qm.addUserToTeam(oidcUser1, team2);
         teams.add(team2);
 
-        Assert.assertArrayEquals(new String[]{"managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com", "anotherManagedUser@Test.com",
-                "anotherLdapUser@Test.com", "anotherOidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "anotherLdapUser@Test.com",
+                        "anotherManagedUser@Test.com",
+                        "anotherOidcUser@Test.com",
+                        "ldapUser@Test.com",
+                        "managedUser@Test.com",
+                        "oidcUser@Test.com");
     }
 
     @Test
     public void testDuplicateUserAsDestination() {
         JsonObject config = configWithDestination("");
 
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder("managedUser@Test.com", "ldapUser@Test.com", "oidcUser@Test.com");
     }
 
     @Test
     public void testEmptyTeamAsDestination() {
         JsonObject config = configWithDestination("");
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
+        Team team = qm.createTeam("testTeam");
         teams.add(team);
-        Assert.assertArrayEquals(null, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId())).isNull();
     }
 
     @Test
     public void testEmptyTeamsAsDestination() {
         JsonObject config = configWithDestination("");
         List<Team> teams = new ArrayList<>();
-        Assert.assertArrayEquals(null, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId())).isNull();
     }
 
     @Test
     public void testEmptyUserEmailsAsDestination() {
         JsonObject config = configWithDestination("");
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(null, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId())).isNull();
     }
 
     @Test
     public void testConfigDestinationAndTeamAsDestination() {
         JsonObject config = configWithDestination("john@doe.com,steve@jobs.org");
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("john@doe.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"john@doe.com", "steve@jobs.org", "managedUser@Test.com", "ldapUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "john@doe.com",
+                        "ldapUser@Test.com",
+                        "managedUser@Test.com",
+                        "steve@jobs.org");
     }
 
     @Test
     public void testNullConfigDestinationAndTeamsDestination() {
         JsonObject config = Json.createObjectBuilder().build();
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("john@doe.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"managedUser@Test.com", "ldapUser@Test.com", "john@doe.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder("managedUser@Test.com", "ldapUser@Test.com", "john@doe.com");
     }
 
     @Test
     public void testEmptyManagedUsersAsDestination() {
         JsonObject config = configWithDestination("john@doe.com,steve@jobs.org");
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setLdapUsers(ldapUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(ldapUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"john@doe.com", "steve@jobs.org", "ldapUser@Test.com", "oidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "john@doe.com",
+                        "ldapUser@Test.com",
+                        "oidcUser@Test.com",
+                        "steve@jobs.org");
     }
 
     @Test
     public void testEmptyLdapUsersAsDestination() {
         JsonObject config = configWithDestination("john@doe.com,steve@jobs.org");
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        OidcUser oidcUser = new OidcUser();
-        oidcUser.setUsername("oidcUserTest");
+        OidcUser oidcUser = qm.createOidcUser("oidcUserTest");
         oidcUser.setEmail("oidcUser@Test.com");
-        List<OidcUser> oidcUsers = new ArrayList<>();
-        oidcUsers.add(oidcUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setOidcUsers(oidcUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(oidcUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"john@doe.com", "steve@jobs.org", "managedUser@Test.com", "oidcUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "john@doe.com",
+                        "managedUser@Test.com",
+                        "oidcUser@Test.com",
+                        "steve@jobs.org");
     }
 
     @Test
     public void testEmptyOidcUsersAsDestination() {
         JsonObject config = configWithDestination("john@doe.com,steve@jobs.org");
-        ManagedUser managedUser = new ManagedUser();
-        managedUser.setUsername("ManagedUserTest");
+        ManagedUser managedUser = qm.createManagedUser("ManagedUserTest", TEST_USER_PASSWORD_HASH);
         managedUser.setEmail("managedUser@Test.com");
-        List<ManagedUser> managedUsers = new ArrayList<>();
-        managedUsers.add(managedUser);
 
-        LdapUser ldapUser = new LdapUser();
-        ldapUser.setUsername("ldapUserTest");
+        LdapUser ldapUser = qm.createLdapUser("ldapUserTest");
         ldapUser.setEmail("ldapUser@Test.com");
-        List<LdapUser> ldapUsers = new ArrayList<>();
-        ldapUsers.add(ldapUser);
 
         List<Team> teams = new ArrayList<>();
-        Team team = new Team();
-        team.setManagedUsers(managedUsers);
-        team.setLdapUsers(ldapUsers);
+        Team team = qm.createTeam("testTeam");
+        qm.addUserToTeam(managedUser, team);
+        qm.addUserToTeam(ldapUser, team);
         teams.add(team);
 
-        Assert.assertArrayEquals(new String[]{"john@doe.com", "steve@jobs.org", "managedUser@Test.com", "ldapUser@Test.com"}, SendMailPublisher.parseDestination(config, teams));
+        NotificationRule rule = createNotificationRule();
+        rule.setTeams(teams);
+
+        assertThat(SendMailPublisher.getDestinations(config, rule.getId()))
+                .containsExactlyInAnyOrder(
+                        "john@doe.com",
+                        "ldapUser@Test.com",
+                        "managedUser@Test.com",
+                        "steve@jobs.org");
     }
+
+    private NotificationRule createNotificationRule() {
+        final NotificationPublisher publisher = qm.createNotificationPublisher(
+                DefaultNotificationPublishers.EMAIL.getPublisherName(),
+                DefaultNotificationPublishers.EMAIL.getPublisherDescription(),
+                DefaultNotificationPublishers.EMAIL.getPublisherClass(),
+                "template",
+                DefaultNotificationPublishers.EMAIL.getTemplateMimeType(),
+                DefaultNotificationPublishers.EMAIL.isDefaultPublisher());
+
+        return qm.createNotificationRule(
+                "testRule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher);
+    }
+
 }