/*
* Copyright 2015 Collective, Inc.
*
* Licensed 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 com.collective.celos;
import com.collective.celos.database.StateDatabaseConnection;
import com.collective.celos.trigger.*;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableMap;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.NativeJavaObject;
import java.io.File;
import java.io.FileReader;
import java.io.StringReader;
import java.util.Properties;
public class JavaScriptFunctionsTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testHourlySchedule() throws Exception {
HourlySchedule s = (HourlySchedule) runJS("celos.hourlySchedule()");
}
@Test
public void testMinutelySchedule() throws Exception {
MinutelySchedule s = (MinutelySchedule) runJS("celos.minutelySchedule()");
}
@Test
public void testCronSchedule() throws Exception {
CronSchedule s = (CronSchedule) runJS("celos.cronSchedule('* 15 * * * ?')");
Assert.assertEquals("* 15 * * * ?", s.getCronExpression());
}
@Test
public void testDependentSchedule() throws Exception {
DependentSchedule s = (DependentSchedule) runJS("celos.dependentSchedule('foo')");
Assert.assertEquals(new WorkflowID("foo"), s.getOtherWorkflowID());
}
@Test
public void testCronScheduleRequiresExpr() throws Exception {
expectMessage("celos.cronSchedule()", "Undefined cron expression (celos-scripts.js#");
}
@Test
public void testSerialSchedulingStrategyDefault() throws Exception {
SerialSchedulingStrategy s = (SerialSchedulingStrategy) runJS("celos.serialSchedulingStrategy()");
Assert.assertEquals(s.getConcurrencyLevel(), 1);
}
@Test
public void testSerialSchedulingStrategy() throws Exception {
SerialSchedulingStrategy s = (SerialSchedulingStrategy) runJS("celos.serialSchedulingStrategy(5)");
Assert.assertEquals(s.getConcurrencyLevel(), 5);
}
@Test
public void testAlwaysTrigger() throws Exception {
AlwaysTrigger t = (AlwaysTrigger) runJS("celos.alwaysTrigger()");
}
@Test
public void testHDFSCheckTrigger() throws Exception {
HDFSCheckTrigger t = (HDFSCheckTrigger) runJS("celos.hdfsCheckTrigger('/foo', 'file:///')");
Assert.assertEquals("/foo", t.getRawPathString());
Assert.assertEquals("file:///", t.getFsString());
}
@Test
public void testHDFSCheckTriggerRequiresPath() {
expectMessage("celos.hdfsCheckTrigger()", "Undefined path in hdfsCheckTrigger (celos-scripts.js#");
}
@Test
public void testHDFSCheckTriggerRequiresFs() {
expectMessage("celos.hdfsCheckTrigger('/foo')", "Undefined fs in hdfsCheckTrigger and CELOS_DEFAULT_HDFS not set (celos-scripts.js#");
}
@Test
public void testHDFSCheckTriggerUsesDefaultNameNode() throws Exception {
HDFSCheckTrigger t = (HDFSCheckTrigger) runJS("var CELOS_DEFAULT_HDFS = 'file:///'; celos.hdfsCheckTrigger('/foo')");
Assert.assertEquals("/foo", t.getRawPathString());
Assert.assertEquals("file:///", t.getFsString());
}
@Test
public void testAndTrigger() throws Exception {
AndTrigger t = (AndTrigger) runJS("celos.andTrigger(celos.delayTrigger(1), celos.alwaysTrigger())");
DelayTrigger dt = (DelayTrigger) t.getTriggers().get(0);
Assert.assertEquals(1, dt.getSeconds());
AlwaysTrigger at = (AlwaysTrigger) t.getTriggers().get(1);
Assert.assertEquals(2, t.getTriggers().size());
}
@Test
public void testOrTrigger() throws Exception {
OrTrigger t = (OrTrigger) runJS("celos.orTrigger(celos.delayTrigger(1), celos.alwaysTrigger())");
DelayTrigger dt = (DelayTrigger) t.getTriggers().get(0);
Assert.assertEquals(1, dt.getSeconds());
AlwaysTrigger at = (AlwaysTrigger) t.getTriggers().get(1);
Assert.assertEquals(2, t.getTriggers().size());
}
@Test
public void testNotTrigger() throws Exception {
NotTrigger t = (NotTrigger) runJS("celos.notTrigger(celos.alwaysTrigger())");
AlwaysTrigger at = (AlwaysTrigger) t.getTrigger();
}
@Test
public void testNotTriggerRequiresSubTrigger() throws Exception {
expectMessage("celos.notTrigger()", "Undefined sub trigger (celos-scripts.js#");
}
@Test
public void testOffsetTrigger() throws Exception {
OffsetTrigger t = (OffsetTrigger) runJS("celos.offsetTrigger(25, celos.alwaysTrigger())");
Assert.assertEquals(25, t.getSeconds());
Assert.assertEquals(AlwaysTrigger.class, t.getTrigger().getClass());
}
@Test
public void testOffsetTriggerWorksWithZeroSeconds() throws Exception {
OffsetTrigger t = (OffsetTrigger) runJS("celos.offsetTrigger(0, celos.alwaysTrigger())");
Assert.assertEquals(0, t.getSeconds());
Assert.assertEquals(AlwaysTrigger.class, t.getTrigger().getClass());
}
@Test
public void testOffsetTriggerRequiresSeconds() throws Exception {
expectMessage("celos.offsetTrigger()", "Undefined seconds (celos-scripts.js#");
}
@Test
public void testOffsetTriggerRequiresSecondsAndTrigger() throws Exception {
expectMessage("celos.offsetTrigger(25)", "Undefined trigger (celos-scripts.js#");
}
@Test
public void testDelayTrigger() throws Exception {
DelayTrigger t = (DelayTrigger) runJS("celos.delayTrigger(25)");
Assert.assertEquals(25, t.getSeconds());
}
@Test
public void testDelayTriggerWorksWithZeroSeconds() throws Exception {
DelayTrigger t = (DelayTrigger) runJS("celos.delayTrigger(0)");
Assert.assertEquals(0, t.getSeconds());
}
@Test
public void testDelayTriggerRequiresSeconds() throws Exception {
expectMessage("celos.delayTrigger()", "Undefined seconds (celos-scripts.js#");
}
@Test
public void testSuccessTrigger() throws Exception {
SuccessTrigger t = (SuccessTrigger) runJS("celos.successTrigger('myworkflow')");
Assert.assertEquals(new WorkflowID("myworkflow"), t.getTriggerWorkflowId());
}
@Test
public void testSuccessTriggerRequiresWorkflowName() throws Exception {
expectMessage("celos.successTrigger()", "Undefined workflow name in success trigger (celos-scripts.js#");
}
@Test
public void testOozieExternalService() throws Exception {
OozieExternalService s = (OozieExternalService) runJS("celos.oozieExternalService({bla:'hello'}, 'http://foo')");
Assert.assertEquals("http://foo", s.getOozieURL());
ObjectNode props = Util.MAPPER.createObjectNode();
props.put("bla", "hello");
Assert.assertEquals(props, s.getProperties(new SlotID(new WorkflowID("foo"), ScheduledTime.now())));
}
@Test
public void testOozieURLRequired() throws Exception {
expectMessage("celos.oozieExternalService({bla:'hello'})", "Undefined Oozie URL (celos-scripts.js#");
}
@Test
public void testOozieURLUsesDefault() throws Exception {
String js = "var CELOS_DEFAULT_OOZIE = 'http://oooooozie'; celos.oozieExternalService({bla:'hello'})";
OozieExternalService s = (OozieExternalService) runJS(js);
Assert.assertEquals("http://oooooozie", s.getOozieURL());
}
@Test
public void testUsesOozieDefaultProperties() throws Exception {
String js = "var CELOS_DEFAULT_OOZIE_PROPERTIES = {a:'1', b:'2'}; celos.oozieExternalService({b: '3', c:'4'}, 'http://oozie')";
OozieExternalService s = (OozieExternalService) runJS(js);
Assert.assertEquals("http://oozie", s.getOozieURL());
ObjectNode props = Util.MAPPER.createObjectNode();
props.put("a", "1");
props.put("b", "3");
props.put("c", "4");
Assert.assertEquals(props, s.getProperties(new SlotID(new WorkflowID("foo"), ScheduledTime.now())));
}
@Test
public void testOoziePropertiesFunction() throws Exception {
String js = "var CELOS_DEFAULT_OOZIE_PROPERTIES = { a: '${year}' }; celos.oozieExternalService(function(slot){ return { b: '${month}', c: new String(slot.getScheduledTime().minusYears(1).year()) }; }, 'http://oozie')";
OozieExternalService s = (OozieExternalService) runJS(js);
Properties props = new Properties();
props.put("a", "2014");
props.put("b", "03");
props.put("c", "2013");
ScheduledTime t = new ScheduledTime("2014-03-01T00:00Z");
Assert.assertEquals(props, s.setupDefaultProperties(s.getProperties(new SlotID(new WorkflowID("foo"), t)), t));
}
@Test
public void testOoziePropertiesFunctionWithJavaObject() throws Exception {
String js = "var CELOS_DEFAULT_OOZIE_PROPERTIES = " +
"{ " +
" a: '${year}' " +
"}; " +
"celos.oozieExternalService(function(slot)" +
" { " +
" return { " +
" b: '${month}', " +
" c: slot.getScheduledTime().minusYears(1).year()" +
" }; " +
"}, 'http://oozie')";
OozieExternalService s = (OozieExternalService) runJS(js);
Properties props = new Properties();
props.put("a", "2014");
props.put("b", "03");
props.put("c", "2013");
ScheduledTime t = new ScheduledTime("2014-03-01T00:00Z");
Assert.assertEquals(props, s.setupDefaultProperties(s.getProperties(new SlotID(new WorkflowID("foo"), t)), t));
}
@Test
public void replaceTimeVariablesWorks() throws Exception {
String s = (String) runJS("celos.replaceTimeVariables('${year}-${month}-${day}T${hour}:${minute}:${second}Z ${year}', new Packages.com.collective.celos.ScheduledTime('2014-05-12T19:33:01Z'))");
Assert.assertEquals("2014-05-12T19:33:01Z 2014", s);
}
@Test
public void testHdfsPathFunction() throws Exception {
String js = "var HDFS_PREFIX_JS_VAR = '/user/celos/test'; \n" +
"celos.hdfsPath('/path')";
String s = (String) runJS(js);
Assert.assertEquals(s, "/user/celos/test/path");
}
@Test
public void testHdfsPathFunctionNoPrefix() throws Exception {
String js = "celos.hdfsPath('/path')";
String s = (String) runJS(js);
Assert.assertEquals(s, "/path");
}
@Test
public void testIsRunningInTestModeFalse() throws Exception {
String js = "celos.isRunningInTestMode()";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, false);
}
@Test
public void testIsRunningInTestModeTrue() throws Exception {
String js = "HDFS_PREFIX_JS_VAR = 'some';" +
"celos.isRunningInTestMode()";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, true);
}
@Test
public void testHdfsCheckNotExists() throws Exception {
String js = "var CELOS_DEFAULT_HDFS = ''; " +
"var schTime = new Packages.com.collective.celos.ScheduledTime('2014-05-12T19:33:01Z');" +
"var workflowId = new Packages.com.collective.celos.WorkflowID('id');" +
"var slotId = new Packages.com.collective.celos.SlotID(workflowId, schTime);" +
"celos.hdfsCheck('/path', slotId)";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, false);
}
@Test
public void testHdfsCheckExists() throws Exception {
String js = "var CELOS_DEFAULT_HDFS = ''; " +
"var schTime = new Packages.com.collective.celos.ScheduledTime('2014-05-12T19:33:01Z');" +
"var workflowId = new Packages.com.collective.celos.WorkflowID('id');" +
"var slotId = new Packages.com.collective.celos.SlotID(workflowId, schTime);" +
"celos.hdfsCheck('file:///tmp', slotId)";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, true);
}
@Test
public void testHdfsCheckExists2() throws Exception {
String js = "var CELOS_DEFAULT_HDFS = ''; " +
"var schTime = new Packages.com.collective.celos.ScheduledTime('2014-05-12T19:33:01Z');" +
"var workflowId = new Packages.com.collective.celos.WorkflowID('id');" +
"var slotId = new Packages.com.collective.celos.SlotID(workflowId, schTime);" +
"celos.hdfsCheck('/tmp', slotId, 'file:///')";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, true);
}
@Test(expected = JavaScriptException.class)
public void testHdfsCheckWrongType() throws Exception {
String js = "var CELOS_DEFAULT_HDFS = ''; " +
"var schTime = new Packages.com.collective.celos.ScheduledTime('2014-05-12T19:33:01Z');" +
"var workflowId = new Packages.com.collective.celos.WorkflowID('id');" +
"var slotId = 'slot';" +
"celos.hdfsCheck('file:///tmp', slotId)";
Boolean s = (Boolean) runJS(js);
Assert.assertEquals(s, true);
}
@Test
public void testRegisters() throws Exception {
WorkflowConfigurationParser parser = createParser();
StateDatabaseConnection conn = new MemoryStateDatabase().openConnection();
ObjectNode v1 = Util.MAPPER.createObjectNode();
v1.put("foo", "bar-Iñtërnâtiônàlizætiøn");
ObjectNode v2 = Util.MAPPER.createObjectNode();
v2.put("quux", "meh-Iñtërnâtiônàlizætiøn");
ObjectNode v3 = Util.MAPPER.createObjectNode();
v3.put("bla", "baz-Iñtërnâtiônàlizætiøn");
conn.putRegister(new BucketID("b1-Iñtërnâtiônàlizætiøn"), new RegisterKey("k1-Iñtërnâtiônàlizætiøn"), v1);
conn.putRegister(new BucketID("b1-Iñtërnâtiônàlizætiøn"), new RegisterKey("k2-Iñtërnâtiônàlizætiøn"), v2);
conn.putRegister(new BucketID("b2-Iñtërnâtiônàlizætiøn"), new RegisterKey("k3-Iñtërnâtiônàlizætiøn"), v3);
File f = new File(Thread.currentThread().getContextClassLoader().getResource("js-tests/test-registers.js").toURI());
parser.evaluateReader(new FileReader(f), f.getName(), conn);
}
private Object runJS(String js) throws Exception {
WorkflowConfigurationParser parser = createParser();
// Evaluate JS function call
Object jsResult = parser.evaluateReader(new StringReader(js), "string", new MemoryStateDatabase().openConnection());
if (jsResult instanceof NativeJavaObject) {
return ((NativeJavaObject) jsResult).unwrap();
} else {
return jsResult;
}
}
private Object runJSNativeResult(String js) throws Exception {
WorkflowConfigurationParser parser = createParser();
// Evaluate JS function call
return parser.evaluateReader(new StringReader(js), "string", new MemoryStateDatabase().openConnection());
}
private WorkflowConfigurationParser createParser() throws Exception {
WorkflowConfigurationParser parser = new WorkflowConfigurationParser(new File("unused"), ImmutableMap.<String, String>of());
return parser;
}
private void expectMessage(String js, String string) throws AssertionError {
try {
runJS(js);
} catch(Exception e) {
if (e.getMessage().contains(string)) {
return;
}
throw new AssertionError(e.getMessage());
}
}
@Test
public void testErrorNotInCelosScripts() throws Exception {
expectMessage("throw \"some exception\";", "some exception (string#1)");
}
}