From 87f7d56e34fcdfe24f0f1378d24fa7d3a2c04b37 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sun, 25 Oct 2026 20:04:34 -0400 Subject: [PATCH 1/4] Add more DestinationSet tests for the current behavior Cover cases that DestinationSet must keep when it is made to implement java.util.Set: - Metadata ids still work when the message has no destination id map, and connector names are not matched. - remove(Collection) and removeAllExcept(Collection) throw a NullPointerException for a null collection. - Calls from JavaScript resolve to the expected overloads for numbers, connector names, and arrays. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Tony Germano --- .../server/userutil/DestinationSetTest.java | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java b/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java index 25f390bf3d..3f2ed5ccce 100644 --- a/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java +++ b/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java @@ -15,6 +15,9 @@ import java.util.Set; import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; import com.mirth.connect.donkey.model.message.ConnectorMessage; import com.mirth.connect.donkey.server.Constants; @@ -57,6 +60,23 @@ private ImmutableConnectorMessage createMessage(Map destination } } + /** + * Runs a JavaScript snippet with destinationSet in scope, as a channel script would. Rhino + * chooses which overload to call, so these tests check the overloads that scripts reach. + * + * @return the result of the script, converted to a boolean + */ + private boolean evaluate(DestinationSet destinationSet, String script) { + Context context = Context.enter(); + try { + Scriptable scope = context.initStandardObjects(); + ScriptableObject.putProperty(scope, "destinationSet", Context.javaToJS(destinationSet, scope)); + return Context.toBoolean(context.evaluateString(scope, script, "test", 1, null)); + } finally { + Context.exit(); + } + } + // --- Tests for remove(Object) --- @Test public void test_removeObject_withSourceMap_removeForMatchingMetadataId() throws Exception { @@ -223,4 +243,109 @@ public void test_removeAllExceptObject_noSourceMap_noRemovalForMatchingConnector assertFalse(destinationSet.removeAllExcept("Seven")); } + + // --- Tests without a destination id map --- + @Test + public void test_removeObject_noDestinationIdMap_removeForMatchingMetadataId() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(null, metaDataIds)); + + assertTrue(destinationSet.remove(3)); + assertFalse(metaDataIds.contains(3)); + assertTrue(metaDataIds.size() == 4); + } + + @Test + public void test_removeObject_noDestinationIdMap_noRemovalForConnectorName() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(null, metaDataIds)); + + assertFalse(destinationSet.remove("Three")); + assertTrue(metaDataIds.size() == 5); + } + + @Test + public void test_removeAllExceptCollection_noDestinationIdMap_retainsMatchingMetadataIds() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(null, metaDataIds)); + + assertTrue(destinationSet.removeAllExcept(Arrays.asList(1, "Five"))); + assertTrue(metaDataIds.size() == 1); + assertTrue(metaDataIds.contains(1)); + } + + // --- Tests for null collections --- + @Test(expected = NullPointerException.class) + public void test_removeCollection_nullCollection_throwsNullPointerException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.remove((Collection) null); + } + + @Test(expected = NullPointerException.class) + public void test_removeAllExceptCollection_nullCollection_throwsNullPointerException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.removeAllExcept((Collection) null); + } + + // --- Tests for calls from JavaScript --- + @Test + public void test_javascript_removeMetadataId() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.remove(3)")); + assertTrue(metaDataIds.size() == 4); + assertFalse(metaDataIds.contains(3)); + } + + @Test + public void test_javascript_removeConnectorName() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.remove('Three')")); + assertTrue(metaDataIds.size() == 4); + assertFalse(metaDataIds.contains(3)); + } + + @Test + public void test_javascript_removeArray() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.remove([1, 'Three', 99])")); + assertTrue(metaDataIds.size() == 3); + assertFalse(metaDataIds.contains(1)); + assertFalse(metaDataIds.contains(3)); + } + + @Test + public void test_javascript_removeAllExceptConnectorName() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.removeAllExcept('Seven')")); + assertTrue(metaDataIds.size() == 1); + assertTrue(metaDataIds.contains(7)); + } + + @Test + public void test_javascript_removeAllExceptArray() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.removeAllExcept([1, 'Five', 99])")); + assertTrue(metaDataIds.size() == 2); + assertTrue(metaDataIds.contains(1)); + assertTrue(metaDataIds.contains(5)); + } + + @Test + public void test_javascript_removeAll() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.removeAll()")); + assertTrue(metaDataIds.isEmpty()); + } } From c5501be415fb91cae9b552949c6c0cb054fdc0dd Mon Sep 17 00:00:00 2001 From: Richard Ogin Date: Sun, 25 Oct 2026 20:09:42 -0400 Subject: [PATCH 2/4] Make DestinationSet implement java.util.Set DestinationSet now extends AbstractSet, so scripts can treat destinationSet as a set of the metaDataIds the message will be sent to. The methods that look up or remove destinations still accept either a metaDataId or a destination connector name. - remove(Object), removeAll(Collection), retainAll(Collection), and contains(Object) are the Set operations. remove(Collection) and removeAllExcept(Collection) stay as aliases of removeAll and retainAll, and removeAllExcept(Object) now delegates to retainAll. - removeAll(Collection), retainAll(Collection), and containsAll throw a NullPointerException for a null collection, as Set requires and as the old methods did. - add and addAll throw UnsupportedOperationException. Destinations can only be removed. - The iterator supports remove, so removeIf also works. - equals, hashCode, and toString follow the Set contract. - When the source map has no destination set, or the message has no destination id map, the fields fall back to empty collections instead of null. Both fields are now final. remove(Collection) and removeAllExcept(Collection) are not deprecated. Without them, Rhino would pass a JavaScript array to remove(Object) or removeAllExcept(Object). The array would not match any destination, so destinationSet.remove([...]) would silently remove nothing and destinationSet.removeAllExcept([...]) would silently remove every destination. This reworks the original commit from #44, 3e60899b6fd3666f2a669cf977fa49eea7cbfa07, to address review comments and to move it to the Gradle source layout. Co-authored-by: Tony Germano Issue: https://github.com/nextgenhealthcare/connect/issues/4414 Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Richard Ogin Signed-off-by: Tony Germano --- .../server/userutil/DestinationSet.java | 225 +++++++++---- .../server/userutil/DestinationSetTest.java | 302 +++++++++++++++++- 2 files changed, 467 insertions(+), 60 deletions(-) diff --git a/server/src/main/java/com/mirth/connect/server/userutil/DestinationSet.java b/server/src/main/java/com/mirth/connect/server/userutil/DestinationSet.java index 738d4c350f..76e1ff6e09 100644 --- a/server/src/main/java/com/mirth/connect/server/userutil/DestinationSet.java +++ b/server/src/main/java/com/mirth/connect/server/userutil/DestinationSet.java @@ -1,17 +1,15 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation +// SPDX-FileCopyrightText: 2025 Richard Ogin +// SPDX-FileCopyrightText: 2026 Tony Germano package com.mirth.connect.server.userutil; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -23,45 +21,55 @@ /** * Utility class used in the preprocessor or source filter/transformer to prevent the message from * being sent to specific destinations. + *

+ * This is a set of the metaDataIds of the destinations the message will be sent to. Methods that + * look up or remove destinations accept either a metaDataId or a destination connector name. + * Destinations cannot be added to the set. */ -public class DestinationSet { +public class DestinationSet extends AbstractSet { - private Map destinationIdMap; - private Set metaDataIds; + private final Map destinationIdMap; + private final Set metaDataIds; /** * DestinationSet instances should NOT be constructed manually. The instance "destinationSet" * provided in the scope should be used. - * + * * @param connectorMessage * The delegate ImmutableConnectorMessage object. */ + @SuppressWarnings("unchecked") public DestinationSet(ImmutableConnectorMessage connectorMessage) { + Map destinationIdMap = null; + Set metaDataIds = null; + try { - if (connectorMessage.getSourceMap().containsKey(Constants.DESTINATION_SET_KEY)) { - this.destinationIdMap = connectorMessage.getDestinationIdMap(); - this.metaDataIds = (Set) connectorMessage.getSourceMap().get(Constants.DESTINATION_SET_KEY); + if (connectorMessage.getSourceMap().get(Constants.DESTINATION_SET_KEY) instanceof Set set) { + destinationIdMap = connectorMessage.getDestinationIdMap(); + metaDataIds = (Set) set; } } catch (Exception e) { } + + this.destinationIdMap = destinationIdMap != null ? destinationIdMap : Collections.emptyMap(); + this.metaDataIds = metaDataIds != null ? metaDataIds : Collections.emptySet(); } /** * Stop a destination from being processed for this message. - * + * * @param metaDataIdOrConnectorName * An integer representing the metaDataId of a destination connector, or the actual * destination connector name. * @return A boolean indicating whether at least one destination connector was actually removed * from processing for this message. */ + @Override public boolean remove(Object metaDataIdOrConnectorName) { - if (metaDataIds != null) { - Integer metaDataId = convertToMetaDataId(metaDataIdOrConnectorName); + Integer metaDataId = convertToMetaDataId(metaDataIdOrConnectorName); - if (metaDataId != null) { - return metaDataIds.remove(metaDataId); - } + if (metaDataId != null) { + return metaDataIds.remove(metaDataId); } return false; @@ -69,7 +77,8 @@ public boolean remove(Object metaDataIdOrConnectorName) { /** * Stop a destination from being processed for this message. - * + * This is the same as {@link #removeAll(Collection)}. + * * @param metaDataIdOrConnectorNames * A collection of integers representing the metaDataId of a destination connectors, * or the actual destination connector names. JavaScript arrays can be used. @@ -77,20 +86,12 @@ public boolean remove(Object metaDataIdOrConnectorName) { * from processing for this message. */ public boolean remove(Collection metaDataIdOrConnectorNames) { - boolean removed = false; - - for (Object metaDataIdOrConnectorName : metaDataIdOrConnectorNames) { - if (remove(metaDataIdOrConnectorName)) { - removed = true; - } - } - - return removed; + return removeAll(metaDataIdOrConnectorNames); } /** * Stop all except one destination from being processed for this message. - * + * * @param metaDataIdOrConnectorName * An integer representing the metaDataId of a destination connector, or the actual * destination connector name. @@ -98,67 +99,173 @@ public boolean remove(Collection metaDataIdOrConnectorNames) { * from processing for this message. */ public boolean removeAllExcept(Object metaDataIdOrConnectorName) { - if (metaDataIds != null) { - Integer metaDataId = convertToMetaDataId(metaDataIdOrConnectorName); + return retainAll(Collections.singleton(metaDataIdOrConnectorName)); + } - Set set = (metaDataId != null) ? Collections.singleton(metaDataId) : Collections.emptySet(); + /** + * Stop all except one destination from being processed for this message. + * This is the same as {@link #retainAll(Collection)}. + * + * @param metaDataIdOrConnectorNames + * A collection of integers representing the metaDataId of a destination connectors, + * or the actual destination connector names. JavaScript arrays can be used. + * @return A boolean indicating whether at least one destination connector was actually removed + * from processing for this message. + */ + public boolean removeAllExcept(Collection metaDataIdOrConnectorNames) { + return retainAll(metaDataIdOrConnectorNames); + } - return metaDataIds.retainAll(set); + /** + * Stop all destinations from being processed for this message. This does NOT mark the source + * message as FILTERED. + *

+ * This is the same as {@link #clear()}, except that it returns whether any destinations were + * removed. + * + * @return A boolean indicating whether at least one destination connector was actually removed + * from processing for this message. + */ + public boolean removeAll() { + if (!metaDataIds.isEmpty()) { + metaDataIds.clear(); + return true; } return false; } /** - * Stop all except one destination from being processed for this message. - * + * Stop one or more destinations from being processed for this message. + * * @param metaDataIdOrConnectorNames * A collection of integers representing the metaDataId of a destination connectors, * or the actual destination connector names. JavaScript arrays can be used. * @return A boolean indicating whether at least one destination connector was actually removed * from processing for this message. */ - public boolean removeAllExcept(Collection metaDataIdOrConnectorNames) { - if (metaDataIds != null) { - Set set = new HashSet(); - - for (Object metaDataIdOrConnectorName : metaDataIdOrConnectorNames) { - Integer metaDataId = convertToMetaDataId(metaDataIdOrConnectorName); + @Override + public boolean removeAll(Collection metaDataIdOrConnectorNames) { + boolean removed = false; - if (metaDataId != null) { - set.add(metaDataId); - } + for (Object metaDataIdOrConnectorName : metaDataIdOrConnectorNames) { + if (remove(metaDataIdOrConnectorName)) { + removed = true; } - - return metaDataIds.retainAll(set); } - return false; + return removed; } /** - * Stop all destinations from being processed for this message. This does NOT mark the source - * message as FILTERED. - * + * Stop all except the given destinations from being processed for this message. + * + * @param metaDataIdOrConnectorNames + * A collection of integers representing the metaDataId of a destination connectors, + * or the actual destination connector names. JavaScript arrays can be used. * @return A boolean indicating whether at least one destination connector was actually removed * from processing for this message. */ - public boolean removeAll() { - if (metaDataIds != null && metaDataIds.size() > 0) { - metaDataIds.clear(); - return true; + @Override + public boolean retainAll(Collection metaDataIdOrConnectorNames) { + Set set = new HashSet(); + + for (Object metaDataIdOrConnectorName : metaDataIdOrConnectorNames) { + Integer metaDataId = convertToMetaDataId(metaDataIdOrConnectorName); + + if (metaDataId != null) { + set.add(metaDataId); + } } - return false; + return metaDataIds.retainAll(set); + } + + /** + * Returns whether a destination will be processed for this message. + * + * @param metaDataIdOrConnectorName + * An integer representing the metaDataId of a destination connector, or the actual + * destination connector name. + * @return A boolean indicating whether the destination connector will be processed for this + * message. + */ + @Override + public boolean contains(Object metaDataIdOrConnectorName) { + return metaDataIds.contains(convertToMetaDataId(metaDataIdOrConnectorName)); + } + + /** + * Returns an iterator over the metaDataIds of the destinations that will be processed for this + * message. The iterator's remove method stops that destination from being processed. + * + * @return An iterator over the metaDataIds in this set. + */ + @Override + public Iterator iterator() { + return metaDataIds.iterator(); + } + + /** + * Returns the number of destinations that will be processed for this message. + * + * @return The number of metaDataIds in this set. + */ + @Override + public int size() { + return metaDataIds.size(); + } + + /** + * Stop all destinations from being processed for this message. This does NOT mark the source + * message as FILTERED. + *

+ * This is the same as {@link #removeAll()}, except that it does not return whether any + * destinations were removed. + */ + @Override + public void clear() { + metaDataIds.clear(); } + /** + * Not supported. Destinations cannot be added to the set. + * + * @throws UnsupportedOperationException + * always + */ + @Override + public boolean add(Integer metaDataId) { + throw new UnsupportedOperationException("Destinations cannot be added to the destination set."); + } + + /** + * Not supported. Destinations cannot be added to the set. + * + * @throws UnsupportedOperationException + * always + */ + @Override + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException("Destinations cannot be added to the destination set."); + } + + /** + * Converts a metaDataId or destination connector name to a metaDataId. + * + * @param metaDataIdOrConnectorName + * A Java or JavaScript number, which is converted to an int, or a destination + * connector name, which is looked up in the destination id map. + * @return The metaDataId, or null if the value is null or is not the name of a destination + * connector. + */ private Integer convertToMetaDataId(Object metaDataIdOrConnectorName) { if (metaDataIdOrConnectorName != null) { if (metaDataIdOrConnectorName instanceof Number) { return ((Number) metaDataIdOrConnectorName).intValue(); } else if (metaDataIdOrConnectorName.getClass().getName().equals("org.mozilla.javascript.NativeNumber")) { return (Integer) Context.jsToJava(metaDataIdOrConnectorName, int.class); - } else if (destinationIdMap != null) { + } else { return destinationIdMap.get(metaDataIdOrConnectorName.toString()); } } diff --git a/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java b/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java index 3f2ed5ccce..ba50533c0b 100644 --- a/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java +++ b/server/src/test/java/com/mirth/connect/server/userutil/DestinationSetTest.java @@ -1,21 +1,27 @@ // SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2025 Richard Ogin -// SPDX-FileCopyrightText: 2025 Tony Germano +// SPDX-FileCopyrightText: 2025-2026 Tony Germano package com.mirth.connect.server.userutil; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.junit.Test; import org.mozilla.javascript.Context; +import org.mozilla.javascript.EvaluatorException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; @@ -348,4 +354,298 @@ public void test_javascript_removeAll() throws Exception { assertTrue(evaluate(destinationSet, "destinationSet.removeAll()")); assertTrue(metaDataIds.isEmpty()); } + + // --- Tests for contains(Object) and containsAll(Collection) --- + @Test + public void test_contains_withSourceMap() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + assertTrue(destinationSet.contains(3)); + assertTrue(destinationSet.contains(3L)); + assertTrue(destinationSet.contains("Three")); + assertFalse(destinationSet.contains(2)); + assertFalse(destinationSet.contains("I_DONT_EXIST")); + assertFalse(destinationSet.contains(null)); + } + + @Test + public void test_contains_noSourceMap() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + + assertFalse(destinationSet.contains(3)); + assertFalse(destinationSet.contains("Three")); + } + + @Test + public void test_containsAll_withSourceMap() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + assertTrue(destinationSet.containsAll(Collections.emptyList())); + assertTrue(destinationSet.containsAll(Arrays.asList(1, "Three", 5))); + assertFalse(destinationSet.containsAll(Arrays.asList(1, 2))); + assertFalse(destinationSet.containsAll(Arrays.asList(1, "I_DONT_EXIST"))); + } + + @Test(expected = NullPointerException.class) + public void test_containsAll_nullCollection_throwsNullPointerException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.containsAll(null); + } + + // --- Tests for removeAll(Collection) --- + @Test + public void test_removeAllCollection_withSourceMap_removeForMatchingItems() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(destinationSet.removeAll(Arrays.asList(1, "Three", 99))); + assertEquals(new HashSet<>(Arrays.asList(5, 7, 9)), metaDataIds); + } + + @Test + public void test_removeAllCollection_withSourceMap_removesEveryMatchingItem() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(destinationSet.removeAll(Arrays.asList(1, 3, 5, 7))); + assertEquals(Collections.singleton(9), metaDataIds); + } + + @Test + public void test_removeAllCollection_withSourceMap_collectionLargerThanSet() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(destinationSet.removeAll(Arrays.asList(1, "Three", 1.0, 97, 98, 99))); + assertEquals(new HashSet<>(Arrays.asList(5, 7, 9)), metaDataIds); + } + + @Test + public void test_removeAllCollection_withSourceMap_noRemovalForNonExistentItems() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertFalse(destinationSet.removeAll(Arrays.asList(100, "OneHundred"))); + assertTrue(metaDataIds.size() == 5); + } + + @Test + public void test_removeAllCollection_noSourceMap_noRemoval() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + assertFalse(destinationSet.removeAll(Arrays.asList(1, "Three"))); + } + + @Test(expected = NullPointerException.class) + public void test_removeAllCollection_nullCollection_throwsNullPointerException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.removeAll((Collection) null); + } + + // --- Tests for retainAll(Collection) --- + @Test + public void test_retainAll_withSourceMap_retainsMatchingItems() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(destinationSet.retainAll(Arrays.asList(1, "Five", 99))); + assertEquals(new HashSet<>(Arrays.asList(1, 5)), metaDataIds); + } + + @Test + public void test_retainAll_withSourceMap_noRemovalWhenAllRetained() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertFalse(destinationSet.retainAll(Arrays.asList(1, "Three", 5, 7, 9))); + assertTrue(metaDataIds.size() == 5); + } + + @Test + public void test_retainAll_noSourceMap_noRemoval() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + assertFalse(destinationSet.retainAll(Arrays.asList(1, "Five"))); + } + + @Test(expected = NullPointerException.class) + public void test_retainAll_nullCollection_throwsNullPointerException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.retainAll(null); + } + + // --- Tests for add(Integer) and addAll(Collection) --- + @Test(expected = UnsupportedOperationException.class) + public void test_add_throwsUnsupportedOperationException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.add(2); + } + + @Test(expected = UnsupportedOperationException.class) + public void test_addAll_throwsUnsupportedOperationException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.addAll(Arrays.asList(2, 4)); + } + + @Test(expected = UnsupportedOperationException.class) + public void test_addAll_emptyCollection_throwsUnsupportedOperationException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + destinationSet.addAll(Collections.emptyList()); + } + + // --- Tests for size(), isEmpty(), and clear() --- + @Test + public void test_size_withSourceMap() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertEquals(5, destinationSet.size()); + assertFalse(destinationSet.isEmpty()); + + metaDataIds.remove(1); + assertEquals(4, destinationSet.size()); + } + + @Test + public void test_size_noSourceMap() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + + assertEquals(0, destinationSet.size()); + assertTrue(destinationSet.isEmpty()); + } + + @Test + public void test_clear_withSourceMap_removesAll() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + destinationSet.clear(); + assertTrue(metaDataIds.isEmpty()); + assertTrue(destinationSet.isEmpty()); + } + + @Test + public void test_clear_noSourceMap() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + + destinationSet.clear(); + assertTrue(destinationSet.isEmpty()); + } + + // --- Tests for iterator() and toArray() --- + @Test + public void test_iterator_withSourceMap_returnsAllMetadataIds() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + Set iterated = new HashSet<>(); + for (Integer metaDataId : destinationSet) { + iterated.add(metaDataId); + } + + assertEquals(createMetadataIds(), iterated); + } + + @Test + public void test_iterator_withSourceMap_removeRemovesMetadataId() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + Iterator iterator = destinationSet.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == 3) { + iterator.remove(); + } + } + + assertEquals(new HashSet<>(Arrays.asList(1, 5, 7, 9)), metaDataIds); + } + + @Test + public void test_removeIf_withSourceMap_removesMatchingMetadataIds() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(destinationSet.removeIf(metaDataId -> metaDataId > 5)); + assertEquals(new HashSet<>(Arrays.asList(1, 3, 5)), metaDataIds); + } + + @Test + public void test_iterator_noSourceMap_isEmpty() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), null)); + assertFalse(destinationSet.iterator().hasNext()); + } + + @Test + public void test_toArray_withSourceMap_returnsAllMetadataIds() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + assertEquals(createMetadataIds(), new HashSet<>(Arrays.asList(destinationSet.toArray()))); + assertEquals(createMetadataIds(), new HashSet<>(Arrays.asList(destinationSet.toArray(new Integer[0])))); + } + + // --- Tests for equals(Object), hashCode(), and toString() --- + @Test + public void test_equals_withSourceMap_equalsSetWithSameMetadataIds() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + assertEquals(createMetadataIds(), destinationSet); + assertEquals(destinationSet, createMetadataIds()); + assertEquals(createMetadataIds().hashCode(), destinationSet.hashCode()); + assertNotEquals(destinationSet, new HashSet<>(Arrays.asList(1, 3))); + } + + @Test + public void test_toString_withSourceMap_listsMetadataIds() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), new LinkedHashSet<>(Arrays.asList(1, 3, 5)))); + assertEquals("[1, 3, 5]", destinationSet.toString()); + } + + // --- Tests for Set operations called from JavaScript --- + @Test + public void test_javascript_contains() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + assertTrue(evaluate(destinationSet, "destinationSet.contains(3)")); + assertTrue(evaluate(destinationSet, "destinationSet.contains('Three')")); + assertFalse(evaluate(destinationSet, "destinationSet.contains(2)")); + } + + @Test + public void test_javascript_removeAllArray() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.removeAll([1, 'Three', 99])")); + assertEquals(new HashSet<>(Arrays.asList(5, 7, 9)), metaDataIds); + } + + @Test + public void test_javascript_removeAllArrayLargerThanSet() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.removeAll([1, 'Three', 97, 98, 99])")); + assertEquals(new HashSet<>(Arrays.asList(5, 7, 9)), metaDataIds); + } + + @Test + public void test_javascript_retainAllArray() throws Exception { + Set metaDataIds = createMetadataIds(); + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), metaDataIds)); + + assertTrue(evaluate(destinationSet, "destinationSet.retainAll([1, 'Five', 99])")); + assertEquals(new HashSet<>(Arrays.asList(1, 5)), metaDataIds); + } + + @Test + public void test_javascript_add_throwsUnsupportedOperationException() throws Exception { + DestinationSet destinationSet = new DestinationSet(createMessage(createDestinationIdMap(), createMetadataIds())); + + try { + evaluate(destinationSet, "destinationSet.add(2)"); + } catch (EvaluatorException e) { + assertTrue(e.getMessage(), e.getMessage().contains("UnsupportedOperationException")); + return; + } + + throw new AssertionError("Expected an exception"); + } } From 5b44382ddf0efd2f728ed2725a598034d691e9fa Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sun, 25 Oct 2026 20:11:41 -0400 Subject: [PATCH 3/4] Generate destinationSet calls with the Set operations The Destination Set Filter step and the script reference list now use destinationSet.removeAll([...]) and destinationSet.retainAll([...]) instead of the remove(Collection) and removeAllExcept(Collection) aliases, so that new scripts use the Set operations. The behavior is the same. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Tony Germano --- .../connect/client/ui/reference/ReferenceListFactory.java | 4 ++-- .../destinationsetfilter/DestinationSetFilterStep.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/main/java/com/mirth/connect/client/ui/reference/ReferenceListFactory.java b/client/src/main/java/com/mirth/connect/client/ui/reference/ReferenceListFactory.java index 7974d33497..54211b2ab4 100644 --- a/client/src/main/java/com/mirth/connect/client/ui/reference/ReferenceListFactory.java +++ b/client/src/main/java/com/mirth/connect/client/ui/reference/ReferenceListFactory.java @@ -329,8 +329,8 @@ private void addCodeTemplateReferences() { addReference(new FunctionReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), null, "Create Segment (in message, index)", "Create a new segment in specified message (msg or tmp) at segment index i", "function createSegment(segmentName, msg, i) {}", new CodeTemplateFunctionDefinition("createSegment", new Parameters("segmentName", "String", "The name of the segment to create.").add("msg", "XML", "The parent XML object in which to create the segment.").add("i", "Number", "The index at which to insert the segment into the parent XML object."), "XML", "An E4X XML object representing the created segment."))); addReference(new FunctionReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), null, "Create Segment After Segment", "Create a new segment and insert it after the target segment", "function createSegmentAfter(insertSegmentName, afterThisSegment) {}", new CodeTemplateFunctionDefinition("createSegmentAfter", new Parameters("insertSegmentName", "String", "The name of the segment to create.").add("afterThisSegment", "XML", "The existing segment that the new segment will be inserted after."), "XML", "An E4X XML object representing the created segment."))); addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Delete Segment", "Delete a segment from the message", "delete ${msg}['${segment}']")); - addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Remove one or more from Destination Set", "Stop one or more destinations from being processed for this message. Only available in the preprocessor or source filter/transformer.", "destinationSet.remove([${metaDataIdOrConnectorNames}]);")); - addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Remove all except specified from Destination Set", "Stop all except the specified destinations from being processed for this message. Only available in the preprocessor or source filter/transformer.", "destinationSet.removeAllExcept([${metaDataIdOrConnectorNames}]);")); + addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Remove one or more from Destination Set", "Stop one or more destinations from being processed for this message. Only available in the preprocessor or source filter/transformer.", "destinationSet.removeAll([${metaDataIdOrConnectorNames}]);")); + addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Remove all except specified from Destination Set", "Stop all except the specified destinations from being processed for this message. Only available in the preprocessor or source filter/transformer.", "destinationSet.retainAll([${metaDataIdOrConnectorNames}]);")); addReference(new ParameterizedCodeReference(CONTEXT_CONNECTOR, Category.MESSAGE.toString(), "Remove all from Destination Set", "Stop all destinations from being processed for this message. Only available in the preprocessor or source filter/transformer.", "destinationSet.removeAll();")); addReference(new ParameterizedCodeReference(CONTEXT_CHANNEL, Category.MESSAGE.toString(), "Message Reprocessed", "Get a variable indicating if this message was reprocessed.", "var reprocessed = sourceMap.get('reprocessed') == true;")); addReference(new ParameterizedCodeReference(CONTEXT_CHANNEL, Category.MESSAGE.toString(), "Message Replaced", "Get a variable indicating if this message was reprocessed and replaced.", "var replaced = sourceMap.get('replaced') == true;")); diff --git a/server/src/main/java/com/mirth/connect/plugins/destinationsetfilter/DestinationSetFilterStep.java b/server/src/main/java/com/mirth/connect/plugins/destinationsetfilter/DestinationSetFilterStep.java index f40e1e04fe..973a84653e 100644 --- a/server/src/main/java/com/mirth/connect/plugins/destinationsetfilter/DestinationSetFilterStep.java +++ b/server/src/main/java/com/mirth/connect/plugins/destinationsetfilter/DestinationSetFilterStep.java @@ -147,9 +147,9 @@ public String getScript(boolean loadFiles) throws ScriptBuilderException { script.append("{\n\tdestinationSet."); if (behavior == Behavior.REMOVE) { - script.append("remove"); + script.append("removeAll"); } else if (behavior == Behavior.REMOVE_ALL_EXCEPT) { - script.append("removeAllExcept"); + script.append("retainAll"); } else { script.append("removeAll"); } From 8f49f8c31686bc32961b86d7bc96826f9e01c9de Mon Sep 17 00:00:00 2001 From: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:36:04 -0600 Subject: [PATCH 4/4] Add a CI fixture for destinationSet One channel with three destinations, six messages: the Destination Set Filter step's generated removeAll([...]) and retainAll([...]), the existing remove([...]) and removeAllExcept(...) calls, and the new size()/contains() and removeIf(). The transformer runs each message that starts with destinationSet as the call under test and writes what it returned and the destinations left into the transformed message, so source_transformed also covers destinations that were removed; the harness can only assert the ones that were reached. Against main's server the first four pass and size()/contains() and removeIf() fail with "Cannot find function". Co-Authored-By: Claude Opus 5.5 Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com> --- .../channels/01-destination-set/channel.xml | 369 ++++++++++++++++++ .../01-filter-step-remove/dest02_status | 1 + .../01-filter-step-remove/dest03_status | 1 + .../messages/01-filter-step-remove/source | 1 + .../01-filter-step-remove/source_transformed | 1 + .../dest03_status | 1 + .../02-filter-step-remove-all-except/source | 1 + .../source_transformed | 1 + .../03-remove-by-name-and-id/dest02_status | 1 + .../messages/03-remove-by-name-and-id/source | 1 + .../source_transformed | 1 + .../04-remove-all-except/dest02_status | 1 + .../messages/04-remove-all-except/source | 1 + .../04-remove-all-except/source_transformed | 1 + .../05-size-and-contains/dest01_status | 1 + .../05-size-and-contains/dest02_status | 1 + .../05-size-and-contains/dest03_status | 1 + .../messages/05-size-and-contains/source | 1 + .../05-size-and-contains/source_transformed | 1 + .../messages/06-remove-if/dest01_status | 1 + .../messages/06-remove-if/source | 1 + .../messages/06-remove-if/source_transformed | 1 + 22 files changed, 390 insertions(+) create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/channel.xml create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest02_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest03_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source_transformed create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/dest03_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source_transformed create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/dest02_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source_transformed create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/dest02_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source_transformed create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest01_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest02_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest03_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source_transformed create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/dest01_status create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source create mode 100644 ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source_transformed diff --git a/ci/tests/130-destination-set/channels/01-destination-set/channel.xml b/ci/tests/130-destination-set/channels/01-destination-set/channel.xml new file mode 100644 index 0000000000..eb5e1e45dc --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/channel.xml @@ -0,0 +1,369 @@ + + 156458a3-9184-47ed-8ed8-abac6599c243 + 4 + Destination set + + 1 + + 0 + sourceConnector + + + + None + true + false + false + 1 + + + Default Resource + [Default Resource] + + + 1000 + + + + + + Remove Dest A + 0 + true + REMOVE + + 1 + + msg + EQUALS + + 'step-remove' + + + + Remove all except Dest C + 1 + true + REMOVE_ALL_EXCEPT + + 3 + + msg + EQUALS + + 'step-remove-all-except' + + + + Run the call and record the destinations left + 2 + true + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + + Channel Reader + SOURCE + true + true + + + + 1 + Dest A + + + + false + false + 10000 + false + 0 + false + false + 1 + + false + + + Default Resource + [Default Resource] + + + 1000 + true + + none + ${message.encodedData} + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + + Channel Writer + DESTINATION + true + true + + + 2 + Dest B + + + + false + false + 10000 + false + 0 + false + false + 1 + + false + + + Default Resource + [Default Resource] + + + 1000 + true + + none + ${message.encodedData} + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + + Channel Writer + DESTINATION + true + true + + + 3 + Dest C + + + + false + false + 10000 + false + 0 + false + false + 1 + + false + + + Default Resource + [Default Resource] + + + 1000 + true + + none + ${message.encodedData} + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + RAW + RAW + + + JavaScript + + + + + + JavaScript + + + + + + + + Channel Writer + DESTINATION + true + true + + + // Modify the message variable below to pre process data +return message; + // This script executes once after a message has been processed +// Responses returned from here will be stored as "Postprocessor" in the response map +return; + // This script executes once when the channel is deployed +// You only have access to the globalMap and globalChannelMap here to persist data +return; + // This script executes once when the channel is undeployed +// You only have access to the globalMap and globalChannelMap here to persist data +return; + + true + DEVELOPMENT + false + false + false + false + false + false + STARTED + true + + + SOURCE + STRING + mirth_source + + + TYPE + STRING + mirth_type + + + + None + + + + + Default Resource + [Default Resource] + + + + + + true + + + America/Chicago + + + true + false + + 1 + + + \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest02_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest02_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest02_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest03_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest03_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/dest03_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source new file mode 100644 index 0000000000..ea83baba87 --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source @@ -0,0 +1 @@ +step-remove \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source_transformed new file mode 100644 index 0000000000..50dee0dc2a --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/01-filter-step-remove/source_transformed @@ -0,0 +1 @@ +step [2, 3] \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/dest03_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/dest03_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/dest03_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source new file mode 100644 index 0000000000..a5c1ffc06c --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source @@ -0,0 +1 @@ +step-remove-all-except \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source_transformed new file mode 100644 index 0000000000..14d59ed881 --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/02-filter-step-remove-all-except/source_transformed @@ -0,0 +1 @@ +step [3] \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/dest02_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/dest02_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/dest02_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source new file mode 100644 index 0000000000..a26abc86ff --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source @@ -0,0 +1 @@ +destinationSet.remove(['Dest A', 3]) \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source_transformed new file mode 100644 index 0000000000..4c38f8095e --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/03-remove-by-name-and-id/source_transformed @@ -0,0 +1 @@ +true [2] \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/dest02_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/dest02_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/dest02_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source new file mode 100644 index 0000000000..d74f98a41c --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source @@ -0,0 +1 @@ +destinationSet.removeAllExcept('Dest B') \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source_transformed new file mode 100644 index 0000000000..4c38f8095e --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/04-remove-all-except/source_transformed @@ -0,0 +1 @@ +true [2] \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest01_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest01_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest01_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest02_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest02_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest02_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest03_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest03_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/dest03_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source new file mode 100644 index 0000000000..eea2157ada --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source @@ -0,0 +1 @@ +destinationSet.size() + ' ' + destinationSet.contains('Dest C') \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source_transformed new file mode 100644 index 0000000000..b9110abc41 --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/05-size-and-contains/source_transformed @@ -0,0 +1 @@ +3 true [1, 2, 3] \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/dest01_status b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/dest01_status new file mode 100644 index 0000000000..59586dad7f --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/dest01_status @@ -0,0 +1 @@ +SENT \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source new file mode 100644 index 0000000000..df56f65b1c --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source @@ -0,0 +1 @@ +destinationSet.removeIf(function (id) { return id > 1; }) \ No newline at end of file diff --git a/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source_transformed b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source_transformed new file mode 100644 index 0000000000..2a6201f9eb --- /dev/null +++ b/ci/tests/130-destination-set/channels/01-destination-set/messages/06-remove-if/source_transformed @@ -0,0 +1 @@ +true [1] \ No newline at end of file