package com.thinkbiganalytics.nifi.v2.spark;
/*-
* #%L
* kylo-nifi-spark-processors
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* 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.
* #L%
*/
import com.thinkbiganalytics.nifi.core.api.metadata.KyloNiFiFlowProvider;
import com.thinkbiganalytics.nifi.core.api.metadata.MetadataProvider;
import com.thinkbiganalytics.nifi.core.api.metadata.MetadataProviderService;
import com.thinkbiganalytics.nifi.core.api.metadata.MetadataRecorder;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.util.MockProcessContext;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
public class ExecuteSparkJobTest {
/**
* Identifier for the metadata provider service
*/
private static final String METADATA_SERVICE_IDENTIFIER = "MockMetadataProviderService";
/**
* Test runner
*/
private final TestRunner runner = TestRunners.newTestRunner(ExecuteSparkJob.class);
/**
* Initialize instance variables.
*/
@Before
public void setUp() {
// Setup test runner
runner.setProperty(ExecuteSparkJob.APPLICATION_JAR, "file:///home/app.jar");
runner.setProperty(ExecuteSparkJob.MAIN_CLASS, "com.example.App");
runner.setProperty(ExecuteSparkJob.MAIN_ARGS, "run");
runner.setProperty(ExecuteSparkJob.SPARK_APPLICATION_NAME, "MyApp");
}
/**
* Verify validators pass with default values from this test class.
*/
@Test
public void testValidators() {
runner.enqueue(new byte[0]);
Assert.assertEquals(0, ((MockProcessContext) runner.getProcessContext()).validate().size());
}
/**
* Verify validators for Data Sources property.
*/
@Test
public void testValidatorsForDatasources() throws Exception {
// Test UUID list validator
runner.setProperty(ExecuteSparkJob.DATASOURCES, "INVALID");
runner.enqueue(new byte[0]);
Set<String> results = ((MockProcessContext) runner.getProcessContext()).validate().stream().map(Object::toString).collect(Collectors.toSet());
Assert.assertEquals(1, results.size());
Assert.assertTrue(results.contains("'Data Sources' validated against 'INVALID' is invalid because not a list of UUIDs"));
// Test missing metadata service
runner.setProperty(ExecuteSparkJob.DATASOURCES, "87870c7e-8ae8-4db4-9959-c2f5a9496833");
runner.enqueue(new byte[0]);
results = ((MockProcessContext) runner.getProcessContext()).validate().stream().map(Object::toString).collect(Collectors.toSet());
Assert.assertEquals(1, results.size());
Assert.assertTrue(results.contains("'Metadata Service' is invalid because Metadata Service is required when Data Sources is not empty"));
// Test with one UUID
final MetadataProviderService metadataService = new MockMetadataProviderService();
runner.addControllerService(METADATA_SERVICE_IDENTIFIER, metadataService);
runner.enableControllerService(metadataService);
runner.setProperty(ExecuteSparkJob.METADATA_SERVICE, METADATA_SERVICE_IDENTIFIER);
runner.enqueue(new byte[0]);
Assert.assertEquals(0, ((MockProcessContext) runner.getProcessContext()).validate().size());
// Test with two UUIDs
runner.setProperty(ExecuteSparkJob.DATASOURCES, "87870c7e-8ae8-4db4-9959-c2f5a9496833,e4562514-8e06-459a-8ea9-1e2630c852f9");
runner.enqueue(new byte[0]);
Assert.assertEquals(0, ((MockProcessContext) runner.getProcessContext()).validate().size());
// Test with expression
runner.setProperty(ExecuteSparkJob.DATASOURCES, "${metadata.dataTransformation.datasourceIds}");
runner.enqueue(new byte[0], Collections.singletonMap("metadata.dataTransformation.datasourceIds", "87870c7e-8ae8-4db4-9959-c2f5a9496833"));
Assert.assertEquals(0, ((MockProcessContext) runner.getProcessContext()).validate().size());
}
/**
* Verify validators for required properties.
*/
@Test
public void testValidatorsWithRequired() {
runner.removeProperty(ExecuteSparkJob.APPLICATION_JAR);
runner.removeProperty(ExecuteSparkJob.MAIN_CLASS);
runner.removeProperty(ExecuteSparkJob.MAIN_ARGS);
runner.removeProperty(ExecuteSparkJob.SPARK_APPLICATION_NAME);
runner.enqueue(new byte[0]);
final Set<String> results = ((MockProcessContext) runner.getProcessContext()).validate().stream().map(Object::toString).collect(Collectors.toSet());
Assert.assertEquals(4, results.size());
Assert.assertTrue(results.contains("'ApplicationJAR' is invalid because ApplicationJAR is required"));
Assert.assertTrue(results.contains("'MainClass' is invalid because MainClass is required"));
Assert.assertTrue(results.contains("'MainArgs' is invalid because MainArgs is required"));
Assert.assertTrue(results.contains("'Spark Application Name' is invalid because Spark Application Name is required"));
}
/**
* A mock implementation of {@link MetadataProviderService} for testing.
*/
private static class MockMetadataProviderService extends AbstractControllerService implements MetadataProviderService {
@Override
public MetadataProvider getProvider() {
return null;
}
@Override
public MetadataRecorder getRecorder() {
return null;
}
@Override
public KyloNiFiFlowProvider getKyloNiFiFlowProvider() {
return null;
}
}
}