http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSqoopActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSqoopActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSqoopActionBuilder.java
new file mode 100644
index 0000000..1bf9e32
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSqoopActionBuilder.java
@@ -0,0 +1,219 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestSqoopActionBuilder extends 
TestNodeBuilderBaseImpl<SqoopAction, SqoopActionBuilder> {
+    private static final String NAME = "sqoop-name";
+    private static final String NAME_NODE = "${nameNode}";
+    private static final String EXAMPLE_DIR = "/path/to/directory";
+    private static final String[] ARGS = {"arg1", "arg2", "arg3"};
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+    private static final String RESOURCE_MANAGER = "${resourceManager}";
+    private static final String PATH_TO_DELETE = "/path/to/delete";
+    private static final String PATH_TO_MKDIR = "/path/to/mkdir";
+
+    @Override
+    protected SqoopActionBuilder getBuilderInstance() {
+        return SqoopActionBuilder.create();
+    }
+
+    @Override
+    protected SqoopActionBuilder getBuilderInstance(final SqoopAction action) {
+        return SqoopActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testResourceManagerAdded() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+        builder.withResourceManager(RESOURCE_MANAGER);
+
+        final SqoopAction action = builder.build();
+        assertEquals(RESOURCE_MANAGER, action.getResourceManager());
+    }
+
+    @Test
+    public void testNameNodeAdded() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+        builder.withNameNode(NAME_NODE);
+
+        final SqoopAction action = builder.build();
+        assertEquals(NAME_NODE, action.getNameNode());
+    }
+
+    @Test
+    public void testPrepareAdded() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+        builder.withPrepare(new 
PrepareBuilder().withDelete(EXAMPLE_DIR).build());
+
+        final SqoopAction action = builder.build();
+        assertEquals(EXAMPLE_DIR, 
action.getPrepare().getDeletes().get(0).getPath());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testSeveralArgumentsAdded() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+
+        for (final String arg : ARGS) {
+            builder.withArgument(arg);
+        }
+
+        final SqoopAction action = builder.build();
+
+        final List<String> argList = action.getArguments();
+        assertEquals(ARGS.length, argList.size());
+
+        for (int i = 0; i < ARGS.length; ++i) {
+            assertEquals(ARGS[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArguments() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArgument(file);
+        }
+
+        builder.withoutArgument(ARGS[0]);
+
+        final SqoopAction action = builder.build();
+
+        final List<String> argList = action.getArguments();
+        final String[] remainingArgs = Arrays.copyOfRange(ARGS, 1, 
ARGS.length);
+        assertEquals(remainingArgs.length, argList.size());
+
+        for (int i = 0; i < remainingArgs.length; ++i) {
+            assertEquals(remainingArgs[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArguments() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArgument(file);
+        }
+
+        builder.clearArguments();
+
+        final SqoopAction action = builder.build();
+
+        final List<String> argList = action.getArguments();
+        assertEquals(0, argList.size());
+    }
+
+    @Test
+    public void testFromExistingSqoopAction() {
+        final SqoopActionBuilder builder = getBuilderInstance();
+
+        builder.withName(NAME)
+                .withResourceManager(RESOURCE_MANAGER)
+                .withNameNode(NAME_NODE)
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT)
+                .withPrepare(new PrepareBuilder()
+                        .withDelete(PATH_TO_DELETE)
+                        .withMkdir(PATH_TO_MKDIR)
+                        .build())
+                .withLauncher(new LauncherBuilder()
+                        .withMemoryMb(1024L)
+                        .withVCores(2L)
+                        .withQueue(DEFAULT)
+                        .withSharelib(DEFAULT)
+                        .withViewAcl(DEFAULT)
+                        .withModifyAcl(DEFAULT)
+                        .build())
+                .withCommand(DEFAULT)
+                .withArgument(ARGS[0])
+                .withArgument(ARGS[1])
+                .withArchive(DEFAULT)
+                .withFile(DEFAULT);
+
+        final SqoopAction action = builder.build();
+
+        final SqoopActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final String newName = "fromExisting_" + NAME;
+        fromExistingBuilder.withName(newName)
+                .withoutArgument(ARGS[1])
+                .withArgument(ARGS[2]);
+
+        final SqoopAction modifiedAction = fromExistingBuilder.build();
+
+        assertEquals(newName, modifiedAction.getName());
+        assertEquals(action.getNameNode(), modifiedAction.getNameNode());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
modifiedAction.getArguments());
+
+        assertEquals(PATH_TO_DELETE, 
modifiedAction.getPrepare().getDeletes().get(0).getPath());
+        assertEquals(PATH_TO_MKDIR, 
modifiedAction.getPrepare().getMkdirs().get(0).getPath());
+
+        assertEquals(1024L, modifiedAction.getLauncher().getMemoryMb());
+        assertEquals(2L, modifiedAction.getLauncher().getVCores());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getQueue());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getSharelib());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getViewAcl());
+        assertEquals(DEFAULT, modifiedAction.getLauncher().getModifyAcl());
+
+        assertEquals(action.getCommand(), modifiedAction.getCommand());
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .build();
+
+        final SqoopAction fromOtherAction = 
SqoopActionBuilder.createFromExistingAction(otherAction)
+                .withName("sqoop")
+                .build();
+
+        assertEquals("sqoop", fromOtherAction.getName());
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSshActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSshActionBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSshActionBuilder.java
new file mode 100644
index 0000000..d1da8fb
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSshActionBuilder.java
@@ -0,0 +1,147 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestSshActionBuilder extends TestNodeBuilderBaseImpl<SshAction, 
SshActionBuilder> {
+    private static final String NAME = "ssh-name";
+    private static final String[] ARGS = {"arg1", "arg2", "arg3"};
+    private static final String DEFAULT = "default";
+
+    @Override
+    protected SshActionBuilder getBuilderInstance() {
+        return SshActionBuilder.create();
+    }
+
+    @Override
+    protected SshActionBuilder getBuilderInstance(final SshAction action) {
+        return SshActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testSeveralArgsAdded() {
+        final SshActionBuilder builder = getBuilderInstance();
+
+        for (final String arg : ARGS) {
+            builder.withArg(arg);
+        }
+
+        final SshAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(ARGS.length, argList.size());
+
+        for (int i = 0; i < ARGS.length; ++i) {
+            assertEquals(ARGS[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testRemoveArgs() {
+        final SshActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.withoutArg(ARGS[0]);
+
+        final SshAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        final String[] remainingArgs = Arrays.copyOfRange(ARGS, 1, 
ARGS.length);
+        assertEquals(remainingArgs.length, argList.size());
+
+        for (int i = 0; i < remainingArgs.length; ++i) {
+            assertEquals(remainingArgs[i], argList.get(i));
+        }
+    }
+
+    @Test
+    public void testClearArgs() {
+        final SshActionBuilder builder = getBuilderInstance();
+
+        for (final String file : ARGS) {
+            builder.withArg(file);
+        }
+
+        builder.clearArgs();
+
+        final SshAction action = builder.build();
+
+        final List<String> argList = action.getArgs();
+        assertEquals(0, argList.size());
+    }
+
+    @Test
+    public void testFromExistingSshAction() {
+        final SshActionBuilder builder = getBuilderInstance();
+
+        builder.withName(NAME)
+                .withCommand(DEFAULT)
+                .withHost(DEFAULT)
+                .withArg(ARGS[0])
+                .withArg(ARGS[1])
+                .withCaptureOutput(true);
+
+        final SshAction action = builder.build();
+
+        final SshActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final String newName = "fromExisting_" + NAME;
+        fromExistingBuilder.withName(newName)
+                .withoutArg(ARGS[1])
+                .withArg(ARGS[2]);
+
+        final SshAction modifiedAction = fromExistingBuilder.build();
+
+        assertEquals(newName, modifiedAction.getName());
+
+        assertEquals(Arrays.asList(ARGS[0], ARGS[2]), 
modifiedAction.getArgs());
+
+        assertEquals(action.getCommand(), modifiedAction.getCommand());
+        assertEquals(action.getHost(), modifiedAction.getHost());
+        assertEquals(action.isCaptureOutput(), 
modifiedAction.isCaptureOutput());
+    }
+
+    @Test
+    public void testFromOtherAction() {
+        final ShellAction parent = ShellActionBuilder.create()
+                .withName("parent")
+                .build();
+
+        final ShellAction otherAction = 
ShellActionBuilder.createFromExistingAction(parent)
+                .withName("shell")
+                .withParent(parent)
+                .build();
+
+        final SshAction fromOtherAction = 
SshActionBuilder.createFromExistingAction(otherAction)
+                .withName("ssh")
+                .build();
+
+        assertEquals("ssh", fromOtherAction.getName());
+        assertEquals(parent, 
fromOtherAction.getParentsWithoutConditions().get(0));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestStreamingBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestStreamingBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestStreamingBuilder.java
new file mode 100644
index 0000000..9102334
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestStreamingBuilder.java
@@ -0,0 +1,127 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestStreamingBuilder {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Test
+    public void testWithMapper() {
+        final String mapperName = "mapper-name.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withMapper(mapperName);
+
+        final Streaming streaming = builder.build();
+        assertEquals(mapperName, streaming.getMapper());
+    }
+
+    @Test
+    public void testWithMapperCalledTwiceThrows() {
+        final String mapperName1 = "mapper-name-2.sh";
+        final String mapperName2 = "mapper-name-2.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withMapper(mapperName1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withMapper(mapperName2);
+    }
+
+    @Test
+    public void testWithReducer() {
+        final String reducerName = "reducer-name.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withReducer(reducerName);
+
+        final Streaming streaming = builder.build();
+        assertEquals(reducerName, streaming.getReducer());
+    }
+
+    @Test
+    public void testWithReducerCalledTwiceThrows() {
+        final String reducerName1 = "reducer-name1.sh";
+        final String reducerName2 = "reducer-name2.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withReducer(reducerName1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withReducer(reducerName2);
+    }
+
+    @Test
+    public void testWithRecordReader() {
+        final String recordReaderName = "record-reader-name.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withRecordReader(recordReaderName);
+
+        final Streaming streaming = builder.build();
+        assertEquals(recordReaderName, streaming.getRecordReader());
+    }
+
+    @Test
+    public void testWithRecordReaderCalledTwiceThrows() {
+        final String recordReaderName1 = "record-reader-name1.sh";
+        final String recordReaderName2 = "record-reader-name2.sh";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withRecordReader(recordReaderName1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withRecordReader(recordReaderName2);
+    }
+
+    @Test
+    public void testWithRecordReaderMapping() {
+        final String mapping1 = "mapping1";
+        final String mapping2 = "mapping2";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withRecordReaderMapping(mapping1)
+               .withRecordReaderMapping(mapping2);
+
+        final Streaming streaming = builder.build();
+        assertEquals(Arrays.asList(mapping1, mapping2), 
streaming.getRecordReaderMappings());
+    }
+
+    @Test
+    public void testWithEnv() {
+        final String env1 = "env1";
+        final String env2 = "env2";
+
+        final StreamingBuilder builder = new StreamingBuilder();
+        builder.withEnv(env1)
+                .withEnv(env2);
+
+        final Streaming streaming = builder.build();
+        assertEquals(Arrays.asList(env1, env2), streaming.getEnvs());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSubWorkflowBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSubWorkflowBuilder.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSubWorkflowBuilder.java
new file mode 100644
index 0000000..d6a99a2
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestSubWorkflowBuilder.java
@@ -0,0 +1,185 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestSubWorkflowBuilder extends 
TestNodeBuilderBaseImpl<SubWorkflowAction, SubWorkflowActionBuilder> {
+    private static final String MAPRED_JOB_QUEUE_NAME = 
"mapred.job.queue.name";
+    private static final String DEFAULT = "default";
+
+    private static final ImmutableMap<String, String> CONFIG_EXAMPLE = 
getConfigExample();
+
+    private static ImmutableMap<String, String> getConfigExample() {
+        final ImmutableMap.Builder<String, String> configExampleBuilder = new 
ImmutableMap.Builder<>();
+
+        final String[] keys = {"mapred.map.tasks", "mapred.input.dir", 
"mapred.output.dir"};
+        final String[] values = {"1", "${inputDir}", "${outputDir}"};
+
+        for (int i = 0; i < keys.length; ++i) {
+            configExampleBuilder.put(keys[i], values[i]);
+        }
+
+        return configExampleBuilder.build();
+    }
+
+    @Override
+    protected SubWorkflowActionBuilder getBuilderInstance() {
+        return SubWorkflowActionBuilder.create();
+    }
+
+    @Override
+    protected SubWorkflowActionBuilder getBuilderInstance(SubWorkflowAction 
action) {
+        return SubWorkflowActionBuilder.createFromExistingAction(action);
+    }
+
+    @Test
+    public void testWithAppPath() {
+        final String appPath = "/path/to/app";
+
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withAppPath(appPath);
+
+        final SubWorkflowAction action = builder.build();
+        assertEquals(appPath, action.getAppPath());
+    }
+
+    @Test
+    public void testWithAppPathCalledTwiceThrows() {
+        final String appPath1 = "/path/to/app1";
+        final String appPath2 = "/path/to/app2";
+
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withAppPath(appPath1);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withAppPath(appPath2);
+    }
+
+    @Test
+    public void testWithPropagatingConfiguration() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withPropagatingConfiguration();
+
+        final SubWorkflowAction action = builder.build();
+        assertEquals(true, action.isPropagatingConfiguration());
+    }
+
+    @Test
+    public void testWithPropagatingConfigurationCalledTwiceThrows() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withPropagatingConfiguration();
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withPropagatingConfiguration();
+    }
+
+    @Test
+    public void testWithoutPropagatingConfiguration() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withPropagatingConfiguration();
+
+        final SubWorkflowAction action = builder.build();
+
+        final SubWorkflowActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        fromExistingBuilder.withoutPropagatingConfiguration();
+
+        final SubWorkflowAction modifiedAction = fromExistingBuilder.build();
+        assertEquals(false, modifiedAction.isPropagatingConfiguration());
+    }
+
+    @Test
+    public void testWithoutPropagatingConfigurationCalledTwiceThrows() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withPropagatingConfiguration();
+
+        final SubWorkflowAction action = builder.build();
+
+        final SubWorkflowActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        fromExistingBuilder.withoutPropagatingConfiguration();
+
+        expectedException.expect(IllegalStateException.class);
+        fromExistingBuilder.withoutPropagatingConfiguration();
+    }
+
+    @Test
+    public void testConfigPropertyAdded() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        final SubWorkflowAction action = builder.build();
+        assertEquals(DEFAULT, 
action.getConfiguration().get(MAPRED_JOB_QUEUE_NAME));
+    }
+
+    @Test
+    public void testSeveralConfigPropertiesAdded() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+
+        for (final Map.Entry<String, String> entry : 
CONFIG_EXAMPLE.entrySet()) {
+            builder.withConfigProperty(entry.getKey(), entry.getValue());
+        }
+
+        final SubWorkflowAction action = builder.build();
+
+        for (final Map.Entry<String, String> entry : 
CONFIG_EXAMPLE.entrySet()) {
+            assertEquals(entry.getValue(), 
action.getConfiguration().get(entry.getKey()));
+        }
+
+        assertEquals(CONFIG_EXAMPLE, action.getConfiguration());
+    }
+
+    @Test
+    public void testSameConfigPropertyAddedTwiceThrows() {
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        expectedException.expect(IllegalStateException.class);
+        builder.withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+    }
+
+    @Test
+    public void testFromExistiongSubWorkflowAction() {
+        final String appPath = "/path/to/app";
+
+        final SubWorkflowActionBuilder builder = getBuilderInstance();
+        builder.withAppPath(appPath)
+                .withPropagatingConfiguration()
+                .withConfigProperty(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+
+        final SubWorkflowAction action = builder.build();
+
+        final SubWorkflowActionBuilder fromExistingBuilder = 
getBuilderInstance(action);
+
+        final SubWorkflowAction modifiedAction = fromExistingBuilder.build();
+        assertEquals(appPath, modifiedAction.getAppPath());
+        assertEquals(true, modifiedAction.isPropagatingConfiguration());
+
+        final Map<String, String> expectedConfiguration = new 
LinkedHashMap<>();
+        expectedConfiguration.put(MAPRED_JOB_QUEUE_NAME, DEFAULT);
+        assertEquals(expectedConfiguration, modifiedAction.getConfiguration());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestTouchz.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestTouchz.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestTouchz.java
new file mode 100644
index 0000000..0bff3a4
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/action/TestTouchz.java
@@ -0,0 +1,33 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestTouchz {
+    @Test
+    public void testGetPath() {
+        final String path = "path";
+        final Touchz touchz = new Touchz(path);
+
+        assertEquals(path, touchz.getPath());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecision.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecision.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecision.java
new file mode 100644
index 0000000..9c12d7e
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecision.java
@@ -0,0 +1,210 @@
+/**
+ * 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.oozie.fluentjob.api.dag;
+
+import org.apache.oozie.fluentjob.api.Condition;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestDecision extends TestNodeBase<Decision> {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Override
+    protected Decision getInstance(final String name) {
+        return new Decision(name);
+    }
+
+    @Test
+    public void testAddParentWhenNoneAlreadyExists() {
+        final Start parent = new Start("parent");
+        final Decision instance = getInstance("instance");
+
+        instance.addParent(parent);
+        assertEquals(parent, instance.getParent());
+        assertEquals(instance, parent.getChild());
+    }
+
+    @Test
+    public void testAddParentWhenItAlreadyExistsThrows() {
+        final NodeBase parent1 = new ExplicitNode("parent1", null);
+        final NodeBase parent2 = new ExplicitNode("parent2", null);
+
+        final Decision instance = getInstance("instance");
+
+        instance.addParent(parent1);
+
+        expectedException.expect(IllegalStateException.class);
+        instance.addParent(parent2);
+    }
+
+    @Test
+    public void testRemoveExistingParent() {
+        final Start parent = new Start("parent");
+        final Decision instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.removeParent(parent);
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testRemoveNonexistentParentThrows() {
+        final Start parent = new Start("parent");
+        final Decision instance = getInstance("instance");
+
+        expectedException.expect(IllegalArgumentException.class);
+        instance.removeParent(parent);
+    }
+
+    @Test
+    public void testClearExistingParent() {
+        final Start parent = new Start("parent");
+        final Decision instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testClearNonExistentParent() {
+        final Start parent = new Start("parent");
+        final Decision instance = getInstance("instance");
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testChildrenWithConditionsAreCorrect() {
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase child2 = new ExplicitNode("child2", null);
+
+        final Decision decision = getInstance("decision");
+
+        final Condition condition1 = Condition.actualCondition("condition1");
+        final Condition condition2 = Condition.actualCondition("condition2");
+
+        child1.addParentWithCondition(decision, condition1);
+        child2.addParentWithCondition(decision, condition2);
+
+        final List<DagNodeWithCondition> childrenWithConditions = 
decision.getChildrenWithConditions();
+
+        assertEquals(2, childrenWithConditions.size());
+
+        assertEquals(child1, childrenWithConditions.get(0).getNode());
+        assertEquals(condition1, childrenWithConditions.get(0).getCondition());
+
+        assertEquals(child2, childrenWithConditions.get(1).getNode());
+        assertEquals(condition2, childrenWithConditions.get(1).getCondition());
+    }
+
+    @Test
+    public void testDefaultChildIsCorrect() {
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase defaultChild = new ExplicitNode("defaultChild", null);
+
+        final Decision decision = getInstance("decision");
+
+        final Condition condition1 = Condition.actualCondition("condition1");
+
+        child1.addParentWithCondition(decision, condition1);
+        defaultChild.addParentDefaultConditional(decision);
+
+        final List<DagNodeWithCondition> childrenWithConditions = 
decision.getChildrenWithConditions();
+
+        assertEquals(2, childrenWithConditions.size());
+
+        assertEquals(child1, childrenWithConditions.get(0).getNode());
+        assertEquals(condition1, childrenWithConditions.get(0).getCondition());
+
+        assertEquals(defaultChild, childrenWithConditions.get(1).getNode());
+        assertTrue(childrenWithConditions.get(1).getCondition().isDefault());
+
+        assertEquals(defaultChild, decision.getDefaultChild());
+    }
+
+    @Test
+    public void testMultipleDefaultChildAddedThrows() {
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase defaultChild1 = new ExplicitNode("defaultChild1", null);
+        final NodeBase defaultChild2 = new ExplicitNode("defaultChild2", null);
+
+        final Decision decision = getInstance("decision");
+
+        final Condition condition1 = Condition.actualCondition("condition1");
+
+        child1.addParentWithCondition(decision, condition1);
+        defaultChild1.addParentDefaultConditional(decision);
+
+        expectedException.expect(IllegalStateException.class);
+        defaultChild2.addParentDefaultConditional(decision);
+    }
+
+    @Test
+    public void testDefaultChildRemovedAndAnotherOneAdded() {
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase defaultChild1 = new ExplicitNode("defaultChild1", null);
+        final NodeBase defaultChild2 = new ExplicitNode("defaultChild2", null);
+
+        final Decision decision = getInstance("decision");
+
+        final Condition condition1 = Condition.actualCondition("condition1");
+
+        child1.addParentWithCondition(decision, condition1);
+        defaultChild1.addParentDefaultConditional(decision);
+
+        defaultChild1.removeParent(decision);
+        defaultChild2.addParentDefaultConditional(decision);
+    }
+
+    @Test
+    public void testDecisionRemovedAsParent() {
+        final Decision instance = getInstance("instance");
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase child2 = new ExplicitNode("child2", null);
+        final NodeBase child3 = new ExplicitNode("child3", null);
+        final NodeBase child4 = new ExplicitNode("child4", null);
+        final NodeBase child5 = new ExplicitNode("child5", null);
+
+        child1.addParentWithCondition(instance, 
Condition.actualCondition("condition"));
+        child2.addParentWithCondition(instance, 
Condition.actualCondition("condition"));
+        child3.addParentWithCondition(instance, 
Condition.actualCondition("condition"));
+        child4.addParentWithCondition(instance, 
Condition.actualCondition("condition"));
+        child5.addParentWithCondition(instance, 
Condition.actualCondition("condition"));
+
+        child5.removeParent(instance);
+
+        assertEquals(Arrays.asList(child1, child2, child3, child4), 
instance.getChildren());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecisionJoin.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecisionJoin.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecisionJoin.java
new file mode 100644
index 0000000..c987d1e
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestDecisionJoin.java
@@ -0,0 +1,36 @@
+/**
+ * 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.oozie.fluentjob.api.dag;
+
+public class TestDecisionJoin extends TestJoiningNodeBase<Decision, 
DecisionJoin> {
+    @Override
+    protected Decision getBranchingInstance(final String name) {
+        return new Decision(name);
+    }
+
+    @Override
+    protected DecisionJoin getJoiningInstance(final String name, final 
Decision branchingPair) {
+        return new DecisionJoin(name, branchingPair);
+    }
+
+    @Override
+    protected JoiningNodeBase<Decision> getInstance(final String name) {
+        return getJoiningInstance(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestEnd.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestEnd.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestEnd.java
new file mode 100644
index 0000000..36adbe0
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestEnd.java
@@ -0,0 +1,117 @@
+/**
+ * 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.oozie.fluentjob.api.dag;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestEnd extends TestNodeBase<End> {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Override
+    protected End getInstance(final String name) {
+        return new End(name);
+    }
+
+    @Test
+    public void testAddParentWhenNoneAlreadyExists() {
+        final Start parent = new Start("parent");
+        final End instance = getInstance("instance");
+
+        instance.addParent(parent);
+        assertEquals(parent, instance.getParent());
+        assertEquals(instance, parent.getChild());
+    }
+
+    @Test
+    public void testAddParentWhenItAlreadyExists() {
+        final Start parent1 = new Start("parent1");
+        final Start parent2 = new Start("parent2");
+        final End instance = getInstance("instance");
+
+        instance.addParent(parent1);
+
+        expectedException.expect(IllegalStateException.class);
+        instance.addParent(parent2);
+    }
+
+    @Test
+    public void testRemoveExistingParent() {
+        final Start parent = new Start("parent");
+        final End instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.removeParent(parent);
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testRemoveNonexistentParentThrows() {
+        final Start parent = new Start("parent");
+        final End instance = getInstance("instance");
+
+        expectedException.expect(IllegalArgumentException.class);
+        instance.removeParent(parent);
+    }
+
+    @Test
+    public void testClearExistingParent() {
+        final Start parent = new Start("parent");
+        final End instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testClearNonExistentParent() {
+        final Start parent = new Start("parent");
+        final End instance = getInstance("instance");
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testAddedAsParentThrows () {
+        final End instance = getInstance("instance");
+        final ExplicitNode child = new ExplicitNode("child", null);
+
+        expectedException.expect(IllegalStateException.class);
+        child.addParent(instance);
+    }
+
+    @Test
+    public void testGetChildren() {
+        final End instance = getInstance("end");
+
+        assertTrue(instance.getChildren().isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestExplicitNode.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestExplicitNode.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestExplicitNode.java
new file mode 100644
index 0000000..5b7ff38
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestExplicitNode.java
@@ -0,0 +1,156 @@
+/**
+ * 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.oozie.fluentjob.api.dag;
+
+import org.apache.oozie.fluentjob.api.action.MapReduceActionBuilder;
+import org.apache.oozie.fluentjob.api.action.Node;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestExplicitNode extends TestNodeBase<ExplicitNode> {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Override
+    protected ExplicitNode getInstance(final String name) {
+        return new ExplicitNode(name, null);
+    }
+
+    @Test
+    public void testRealNode() {
+        final Node node = MapReduceActionBuilder.create().build();
+        final ExplicitNode instance = new ExplicitNode(NAME, node);
+
+        assertEquals(node, instance.getRealNode());
+    }
+
+    @Test
+    public void testAddParentWhenNoneAlreadyExists() {
+        final ExplicitNode parent = getInstance("parent");
+        final ExplicitNode instance = getInstance("instance");
+
+        instance.addParent(parent);
+        assertEquals(parent, instance.getParent());
+        assertEquals(instance, parent.getChild());
+    }
+
+    @Test
+    public void testAddingParentWhenItAlreadyExistsThrows() {
+        final NodeBase parent1 = getInstance("parent1");
+        final NodeBase parent2 = getInstance("parent2");
+
+        final ExplicitNode instance = getInstance("instance");
+
+        instance.addParent(parent1);
+
+        expectedException.expect(IllegalStateException.class);
+        instance.addParent(parent2);
+    }
+
+    @Test
+    public void testRemoveExistingParent() {
+        final ExplicitNode parent = getInstance("parent");
+        final ExplicitNode instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.removeParent(parent);
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testRemoveNonexistentParentThrows() {
+        final ExplicitNode parent = getInstance("parent");
+        final ExplicitNode instance = getInstance("instance");
+
+        expectedException.expect(IllegalArgumentException.class);
+        instance.removeParent(parent);
+    }
+
+    @Test
+    public void testClearExistingParent() {
+        final Start parent = new Start("parent");
+        final ExplicitNode instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testClearNonExistentParent() {
+        final Start parent = new Start("parent");
+        final ExplicitNode instance = getInstance("instance");
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testNormalAddedAsParentWhenItHasNoChild() {
+        final ExplicitNode instance = getInstance("start");
+        final NodeBase child = getInstance("child");
+
+        child.addParent(instance);
+
+        assertEquals(child, instance.getChild());
+    }
+
+    @Test
+    public void testNormalAddedAsParentWhenItAlreadyHasAChildThrows() {
+        final ExplicitNode instance = getInstance("instance");
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase child2 = new ExplicitNode("child2", null);
+
+        child1.addParent(instance);
+
+        expectedException.expect(IllegalStateException.class);
+        child2.addParent(instance);
+    }
+
+    @Test
+    public void testNormalRemovedAsParent() {
+        final ExplicitNode instance = getInstance("instance");
+        final NodeBase child = getInstance("child");
+
+        child.addParent(instance);
+        child.removeParent(instance);
+
+        assertEquals(null, instance.getChild());
+    }
+
+    @Test
+    public void testGetChildren() {
+        final ExplicitNode instance = getInstance("start");
+        final NodeBase child = getInstance("child");
+
+        child.addParent(instance);
+
+        assertEquals(Arrays.asList(child), instance.getChildren());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestFork.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestFork.java
 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestFork.java
new file mode 100644
index 0000000..f6a2666
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/test/java/org/apache/oozie/fluentjob/api/dag/TestFork.java
@@ -0,0 +1,156 @@
+/**
+ * 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.oozie.fluentjob.api.dag;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TestFork extends TestNodeBase<Fork> {
+    @Rule
+    public final ExpectedException expectedException = 
ExpectedException.none();
+
+    @Override
+    protected Fork getInstance(final String name) {
+        return new Fork(name);
+    }
+
+    @Test
+    public void testAddParentWhenNoneAlreadyExists() {
+        final Start parent = new Start("parent");
+        final Fork instance = getInstance("instance");
+
+        instance.addParent(parent);
+        assertEquals(parent, instance.getParent());
+        assertEquals(instance, parent.getChild());
+    }
+
+    @Test
+    public void testAddParentWhenItAlreadyExistsThrows() {
+        final NodeBase parent1 = getInstance("parent1");
+        final NodeBase parent2 = getInstance("parent2");
+
+        final Fork instance = getInstance("instance");
+
+        instance.addParent(parent1);
+
+        expectedException.expect(IllegalStateException.class);
+        instance.addParent(parent2);
+    }
+
+    @Test
+    public void testRemoveExistingParent() {
+        final Start parent = new Start("parent");
+        final Fork instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.removeParent(parent);
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testRemoveNonexistentParentThrows() {
+        final Start parent = new Start("parent");
+        final Fork instance = getInstance("instance");
+
+        expectedException.expect(IllegalArgumentException.class);
+        instance.removeParent(parent);
+    }
+
+    @Test
+    public void testClearExistingParent() {
+        final Start parent = new Start("parent");
+        final Fork instance = getInstance("instance");
+
+        instance.addParent(parent);
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testClearNonExistentParent() {
+        final Start parent = new Start("parent");
+        final Fork instance = getInstance("instance");
+
+        instance.clearParents();
+        assertEquals(null, instance.getParent());
+        assertEquals(null, parent.getChild());
+    }
+
+    @Test
+    public void testForkAddedAsParentWhenItHasNoChild() {
+        final Fork instance = getInstance("instance");
+        final NodeBase child = getInstance("child");
+
+        child.addParent(instance);
+
+        assertEquals(Arrays.asList(child), instance.getChildren());
+    }
+
+    @Test
+    public void testForkAddedAsParentWhenItAlreadyHasAChild() {
+        final Fork instance = getInstance("instance");
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase child2 = new ExplicitNode("child2", null);
+
+        child1.addParent(instance);
+        child2.addParent(instance);
+
+        assertEquals(Arrays.asList(child1, child2), instance.getChildren());
+    }
+
+    @Test
+    public void testForkRemovedAsParent() {
+        final Fork instance = getInstance("instance");
+        final NodeBase child1 = new ExplicitNode("child1", null);
+        final NodeBase child2 = new ExplicitNode("child2", null);
+        final NodeBase child3 = new ExplicitNode("child3", null);
+        final NodeBase child4 = new ExplicitNode("child4", null);
+        final NodeBase child5 = new ExplicitNode("child5", null);
+
+        child1.addParent(instance);
+        child2.addParent(instance);
+        child3.addParent(instance);
+        child4.addParent(instance);
+        child5.addParent(instance);
+
+        child5.removeParent(instance);
+
+        assertEquals(Arrays.asList(child1, child2, child3, child4), 
instance.getChildren());
+    }
+
+    @Test
+    public void testClose() {
+        final Fork instance = getInstance("instance");
+
+        final Join join = new Join("join", instance);
+
+        assertEquals(join, instance.getClosingJoin());
+        assertTrue(instance.isClosed());
+    }
+}

Reply via email to