blob: f583c30ab76d665790a8d18516d88dd7a39565f1 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.metron.profiler;
import org.adrianwalker.multilinestring.Multiline;
import org.apache.metron.common.configuration.profiler.ProfilerConfig;
import org.apache.metron.common.utils.JSONUtils;
import org.apache.metron.stellar.dsl.Context;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class DefaultMessageRouterTest {
/**
* {
* "ip_src_addr": "10.0.0.1",
* "value": "22"
* }
*/
@Multiline
private String inputOne;
private JSONObject messageOne;
/**
* {
* "ip_src_addr": "10.0.0.2",
* "value": "22"
* }
*/
@Multiline
private String inputTwo;
private JSONObject messageTwo;
/**
* {
* "ip_src_addr": "10.0.0.1",
* "value": "22",
* "timestamp": 1531250226659
* }
*/
@Multiline
private String inputWithTimestamp;
private JSONObject messageWithTimestamp;
/**
* {
* "profiles": [ ]
* }
*/
@Multiline
private String noProfiles;
/**
* {
* "profiles": [
* {
* "profile": "profile-one",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String oneProfile;
/**
* {
* "profiles": [
* {
* "profile": "profile-one",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* },
* {
* "profile": "profile-two",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String twoProfiles;
/**
* {
* "profiles": [
* {
* "profile": "profile-one",
* "onlyif": "false",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String exclusiveProfile;
/**
* {
* "profiles": [
* {
* "profile": "bad-profile",
* "foreach": "2 / 0",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String badForeach;
/**
* {
* "profiles": [
* {
* "profile": "bad-profile",
* "onlyif": "2 / 0",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String badOnlyif;
/**
* {
* "profiles": [
* {
* "profile": "bad-profile",
* "foreach": "2 / 0",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* },
* {
* "profile": "good-profile",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ]
* }
*/
@Multiline
private String goodAndBad;
/**
* {
* "profiles": [
* {
* "profile": "profile-one",
* "foreach": "ip_src_addr",
* "init": { "x": "0" },
* "update": { "x": "x + 1" },
* "result": "x"
* }
* ],
* "timestampField": "timestamp"
* }
*/
@Multiline
private String profileWithEventTime;
private DefaultMessageRouter router;
private Context context;
/**
* Creates a profile definition based on a string of JSON.
* @param json The string of JSON.
*/
private ProfilerConfig createConfig(String json) throws IOException {
return JSONUtils.INSTANCE.load(json, ProfilerConfig.class);
}
@Before
public void setup() throws Exception {
this.router = new DefaultMessageRouter(Context.EMPTY_CONTEXT());
this.context = Context.EMPTY_CONTEXT();
JSONParser parser = new JSONParser();
this.messageOne = (JSONObject) parser.parse(inputOne);
this.messageTwo = (JSONObject) parser.parse(inputTwo);
this.messageWithTimestamp = (JSONObject) parser.parse(inputWithTimestamp);
}
@Test
public void testWithOneRoute() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(oneProfile), context);
assertEquals(1, routes.size());
MessageRoute route1 = routes.get(0);
assertEquals(messageOne.get("ip_src_addr"), route1.getEntity());
assertEquals("profile-one", route1.getProfileDefinition().getProfile());
}
@Test
public void testWithNoRoutes() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(noProfiles), context);
assertEquals(0, routes.size());
}
@Test
public void testWithTwoRoutes() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(twoProfiles), context);
assertEquals(2, routes.size());
{
MessageRoute route1 = routes.get(0);
assertEquals(messageOne.get("ip_src_addr"), route1.getEntity());
assertEquals("profile-one", route1.getProfileDefinition().getProfile());
}
{
MessageRoute route2 = routes.get(1);
assertEquals(messageOne.get("ip_src_addr"), route2.getEntity());
assertEquals("profile-two", route2.getProfileDefinition().getProfile());
}
}
/**
* The 'onlyif' condition should exclude some messages from being routed to a profile.
*/
@Test
public void testExclusiveProfile() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(exclusiveProfile), context);
assertEquals(0, routes.size());
}
/**
* If a profile has a bad foreach expression, any exceptions need caught and the profile needs to be ignored.
*/
@Test
public void testWithBadForeachExpression() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(badForeach), context);
assertEquals(0, routes.size());
}
/**
* If a profile has a bad foreach expression, any exceptions need caught and the profile needs to be ignored.
*/
@Test
public void testWithBadOnlyifExpression() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(badForeach), context);
assertEquals(0, routes.size());
}
/**
* What happens if there are good and bad profiles? The good profiles need routes, the bad profiles need
* to be ignored.
*/
@Test
public void testWithGoodAndBad() throws Exception {
List<MessageRoute> routes = router.route(messageOne, createConfig(goodAndBad), context);
assertEquals(1, routes.size());
MessageRoute route1 = routes.get(0);
assertEquals("good-profile", route1.getProfileDefinition().getProfile());
assertEquals(messageOne.get("ip_src_addr"), route1.getEntity());
}
/**
*
*/
@Test
public void testMessageWithTimestamp() throws Exception {
List<MessageRoute> routes = router.route(messageWithTimestamp, createConfig(profileWithEventTime), context);;
assertEquals(1, routes.size());
MessageRoute route1 = routes.get(0);
assertEquals("profile-one", route1.getProfileDefinition().getProfile());
assertEquals(messageWithTimestamp.get("ip_src_addr"), route1.getEntity());
assertEquals(messageWithTimestamp.get("timestamp"), route1.getTimestamp());
}
/**
* If the timestamp of a message cannot be determined, it should not be routed.
*
* <p>This might happen when using event time and the message is missing the timestamp field.
*/
@Test
public void testMessageWithMissingTimestamp() throws Exception {
// messageOne does not contain a timestamp
List<MessageRoute> routes = router.route(messageOne, createConfig(profileWithEventTime), context);
assertEquals(0, routes.size());
}
}