/*
* 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.Trigger;
import com.collective.celos.trigger.TriggerStatus;
import com.google.common.collect.ImmutableMap;
import org.junit.Assert;
import org.junit.Test;
import org.mozilla.javascript.NativeJavaObject;
import java.io.File;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.SortedSet;
public class WorkflowConfigurationParserTest {
@Test
public void emptyDirCreatesEmptyWorkflowConfiguration() throws Exception {
WorkflowConfiguration cfg = parseDir("empty");
Assert.assertEquals(0, cfg.getWorkflows().size());
}
@Test
public void failsOnIllFormattedFile() throws Exception {
expectMessage("ill-formatted", "missing ; before statement");
}
public static class TestSchedule implements Schedule {
@Override
public SortedSet<ScheduledTime> getScheduledTimes(Scheduler scheduler, ScheduledTime start, ScheduledTime end) {
return null;
}
}
public static class TestSchedulingStrategy implements SchedulingStrategy {
@Override
public List<SlotState> getSchedulingCandidates(List<SlotState> states) {
return null;
}
}
public static class TestExternalService implements ExternalService {
@Override
public String submit(SlotID id) throws ExternalServiceException {
return null;
}
@Override
public void start(SlotID id, String externalID) throws ExternalServiceException {
}
@Override
public ExternalStatus getStatus(SlotID slotId, String externalWorkflowID) throws ExternalServiceException {
return null;
}
@Override
public void kill(SlotID id, String externalID) throws ExternalServiceException {
}
}
public static class TestTrigger extends Trigger {
@Override
public TriggerStatus getTriggerStatus(StateDatabaseConnection connection, ScheduledTime now, ScheduledTime scheduledTime) throws Exception {
return makeTriggerStatus(false, "TestTrigger");
}
}
@Test
public void propertiesAreCorrectlySet() throws Exception {
WorkflowConfiguration cfg = parseFile("properties-test");
Workflow wf1 = cfg.findWorkflow(new WorkflowID("workflow-1"));
Assert.assertEquals("workflow-1", wf1.getID().toString());
Assert.assertEquals(55, wf1.getMaxRetryCount());
Assert.assertEquals(Workflow.DEFAULT_START_TIME, wf1.getStartTime());
Assert.assertEquals(Workflow.DEFAULT_WAIT_TIMEOUT_SECONDS, wf1.getWaitTimeoutSeconds());
Workflow wf2 = cfg.findWorkflow(new WorkflowID("workflow-2"));
Assert.assertEquals("workflow-2", wf2.getID().toString());
Assert.assertEquals(66, wf2.getMaxRetryCount());
Assert.assertEquals(new ScheduledTime("2014-03-10T12:34:56.789Z"), wf2.getStartTime());
Assert.assertEquals(23, wf2.getWaitTimeoutSeconds());
}
@Test
public void workflowInfoWF1() throws Exception {
WorkflowConfiguration cfg = parseFile("workflow-info-test");
WorkflowInfo workflowInfo = cfg.findWorkflow(new WorkflowID("workflow-1")).getWorkflowInfo();
Assert.assertNull(workflowInfo.getUrl());
Assert.assertTrue(workflowInfo.getContacts().isEmpty());
}
@Test
public void workflowInfoWF2() throws Exception {
WorkflowConfiguration cfg = parseFile("workflow-info-test");
WorkflowInfo workflowInfo = cfg.findWorkflow(new WorkflowID("workflow-2")).getWorkflowInfo();
Assert.assertEquals(new URL("http://collective.com/workflow"), workflowInfo.getUrl());
Assert.assertEquals(1, workflowInfo.getContacts().size());
Assert.assertEquals("John Doe", workflowInfo.getContacts().get(0).getName());
Assert.assertEquals(URI.create("john.doe@collective.com"), workflowInfo.getContacts().get(0).getEmail());
}
@Test
public void workflowInfoWFNoName() throws Exception {
WorkflowConfiguration cfg = parseFile("workflow-info-test");
WorkflowInfo workflowInfo = cfg.findWorkflow(new WorkflowID("workflow-3")).getWorkflowInfo();
Assert.assertEquals(new URL("http://collective.com/workflow"), workflowInfo.getUrl());
Assert.assertEquals(1, workflowInfo.getContacts().size());
Assert.assertNull(workflowInfo.getContacts().get(0).getName());
Assert.assertEquals(URI.create("john.doe@collective.com"), workflowInfo.getContacts().get(0).getEmail());
}
@Test
public void workflowInfoNoEmail() throws Exception {
WorkflowConfiguration cfg = parseFile("workflow-info-test");
WorkflowInfo workflowInfo = cfg.findWorkflow(new WorkflowID("workflow-4")).getWorkflowInfo();
Assert.assertEquals(new URL("http://collective.com/workflow"), workflowInfo.getUrl());
Assert.assertEquals(1, workflowInfo.getContacts().size());
Assert.assertEquals("John Doe", workflowInfo.getContacts().get(0).getName());
Assert.assertNull(workflowInfo.getContacts().get(0).getEmail());
}
@Test
public void workflowInfoNoContacts() throws Exception {
WorkflowConfiguration cfg = parseFile("workflow-info-test");
WorkflowInfo workflowInfo = cfg.findWorkflow(new WorkflowID("workflow-5")).getWorkflowInfo();
Assert.assertEquals(new URL("http://collective.com/workflow"), workflowInfo.getUrl());
Assert.assertTrue(workflowInfo.getContacts().isEmpty());
}
@Test
public void canFindWorkflow() throws Exception {
WorkflowConfiguration cfg = parseDir("properties-test");
Workflow wf1 = cfg.findWorkflow(new WorkflowID("workflow-1"));
Assert.assertEquals(55, wf1.getMaxRetryCount());
Assert.assertNull(cfg.findWorkflow(new WorkflowID("foobar")));
}
@Test
public void idMustExist() throws Exception {
expectMessage("id-missing", "Workflow ID must be a string");
}
@Test
public void idMustBeAString() throws Exception {
expectMessage("id-not-a-string", "Workflow ID must be a string");
}
@Test
public void maxRetryCountUsesZeroIfNotSet() throws Exception {
Workflow wf = parseDir("maxretrycount-missing").findWorkflow(new WorkflowID("workflow-1"));
Assert.assertEquals(0, wf.getMaxRetryCount());
}
@Test
public void maxRetryCountMustBeANumber() throws Exception {
expectMessage("maxretrycount-not-a-number", "Cannot convert foo to java.lang.Integer");
}
@Test
public void doesntFailOnSingleWorkflowError() throws Exception {
WorkflowConfiguration cfg = parseDir("single-error");
Assert.assertEquals(2, cfg.getWorkflows().size());
Assert.assertNotNull(cfg.findWorkflow(new WorkflowID("workflow-1")));
Assert.assertNotNull(cfg.findWorkflow(new WorkflowID("workflow-3")));
}
@Test
public void doesntAllowDuplicateIDs() throws Exception {
// Directory contains 2 workflows, but one will be dropped because of duplicate ID.
WorkflowConfiguration cfg = parseFile("duplicate-ids");
Assert.assertEquals(1, cfg.getWorkflows().size());
}
@Test
public void scopesAreSeparate() throws Exception {
parseNamedFile("separate-scopes", "workflow-1");
parseNamedFile("separate-scopes", "workflow-2");
}
@Test
public void defaultsWork() throws Exception {
parseNamedFile("uses-defaults", "workflow-1");
}
@Test
public void evaluatesAdditionalVar() throws Exception {
WorkflowConfigurationParser parser = new WorkflowConfigurationParser(new File("unused"), ImmutableMap.of("var1", "val1"));
// Evaluate JS function call
Object jsResult = parser.evaluateReader(new StringReader("var1"), "string", new MemoryStateDatabase().openConnection());
Assert.assertEquals(jsResult, "val1");
}
@Test
public void testTakesDefaultUsername() throws Exception {
URL resource = Thread.currentThread().getContextClassLoader().getResource("com/collective/celos/defaults-oozie-props");
File defaults = new File(resource.toURI());
WorkflowConfigurationParser parser = new WorkflowConfigurationParser(defaults, ImmutableMap.of("var1", "val1"));
String func = "function (slotId) {" +
" return {" +
" \"oozie.wf.application.path\": \"/workflow.xml\"," +
" \"inputDir\": \"/input\"," +
" \"outputDir\": \"/output\"" +
" }" +
" }";
String str = "importDefaults(\"test\"); celos.makePropertiesGen(" + func + "); ";
NativeJavaObject jsResult = (NativeJavaObject) parser.evaluateReader(new StringReader(str), "string", new MemoryStateDatabase().openConnection());
PropertiesGenerator generator = (PropertiesGenerator) jsResult.unwrap();
Assert.assertEquals(generator.getProperties(null).get("user.name").asText(), "default");
}
@Test
public void testTakesChangesUsername() throws Exception {
URL resource = Thread.currentThread().getContextClassLoader().getResource("com/collective/celos/defaults-oozie-props");
File defaults = new File(resource.toURI());
WorkflowConfigurationParser parser = new WorkflowConfigurationParser(defaults, ImmutableMap.of("CELOS_USER_JS_VAR", "nameIsChanged"));
String func = "function (slotId) {" +
" return {" +
" \"oozie.wf.application.path\": \"/workflow.xml\"," +
" \"inputDir\": \"/input\"," +
" \"outputDir\": \"/output\"" +
" }" +
" }";
String str = "importDefaults(\"test\"); celos.makePropertiesGen(" + func + "); ";
NativeJavaObject jsResult = (NativeJavaObject) parser.evaluateReader(new StringReader(str), "string", new MemoryStateDatabase().openConnection());
PropertiesGenerator generator = (PropertiesGenerator) jsResult.unwrap();
Assert.assertEquals(generator.getProperties(null).get("user.name").asText(), "nameIsChanged");
}
@Test
public void doesntEvaluateAdditionalVar() throws Exception {
WorkflowConfigurationParser parser = new WorkflowConfigurationParser(new File("unused"), ImmutableMap.<String, String>of());
try {
parser.evaluateReader(new StringReader("var1"), "string", new MemoryStateDatabase().openConnection());
} catch (Exception e) {
if (e.getMessage().contains("\"var1\" is not defined")) {
return;
}
}
Assert.fail();
}
public static WorkflowConfiguration parseFile(String label) throws Exception {
return parseNamedFile(label, "workflow-1");
}
private static WorkflowConfiguration parseNamedFile(String label, String workflowName) throws URISyntaxException,
Exception {
File dir = getConfigurationDir(label);
File defaults = getDefaultsDir();
File workflow = new File(dir, workflowName + "." + WorkflowConfigurationParser.WORKFLOW_FILE_EXTENSION);
WorkflowConfigurationParser workflowConfigurationParser = new WorkflowConfigurationParser(defaults, ImmutableMap.<String, String>of());
workflowConfigurationParser.parseFile(workflow, new MemoryStateDatabase().openConnection());
return workflowConfigurationParser.getWorkflowConfiguration();
}
public static WorkflowConfiguration parseDir(String label) throws Exception {
File dir = getConfigurationDir(label);
File defaults = getDefaultsDir();
return new WorkflowConfigurationParser(defaults, ImmutableMap.<String, String>of()).parseConfiguration(dir, new MemoryStateDatabase().openConnection()).getWorkflowConfiguration();
}
public static File getConfigurationDir(String label) throws URISyntaxException {
String path = "com/collective/celos/workflow-configuration-test/" + label;
URL resource = Thread.currentThread().getContextClassLoader().getResource(path);
return new File(resource.toURI());
}
public static File getDefaultsDir() throws URISyntaxException {
String path = "com/collective/celos/defaults";
URL resource = Thread.currentThread().getContextClassLoader().getResource(path);
return new File(resource.toURI());
}
private void expectMessage(String label, String message) throws AssertionError {
try {
parseFile(label);
} catch(Exception e) {
if (e.getMessage().contains(message)) {
return;
}
}
throw new AssertionError();
}
}