package com.thinkbiganalytics.spark.service; /*- * #%L * thinkbig-spark-shell-client-app * %% * 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.spark.SparkContextService; import com.thinkbiganalytics.spark.dataprofiler.Profiler; import com.thinkbiganalytics.spark.metadata.TransformScript; import com.thinkbiganalytics.spark.repl.SparkScriptEngine; import com.thinkbiganalytics.spark.rest.model.Datasource; import com.thinkbiganalytics.spark.rest.model.TransformRequest; import com.thinkbiganalytics.spark.rest.model.TransformResponse; import com.thinkbiganalytics.spark.shell.DatasourceProvider; import com.thinkbiganalytics.spark.shell.DatasourceProviderFactory; import org.apache.commons.io.IOUtils; import org.apache.spark.SparkContext; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import javax.annotation.Nonnull; import scala.tools.nsc.interpreter.NamedParam; public class TransformServiceTest { /** * Verify executing a transformation request. */ @Test @SuppressWarnings("unchecked") public void execute() throws Exception { // Mock Spark context service final SparkContextService sparkContextService = Mockito.mock(SparkContextService.class); // Mock Spark script engine final SparkScriptEngine engine = Mockito.mock(SparkScriptEngine.class); Mockito.when(engine.eval(Mockito.anyString(), Mockito.anyListOf(NamedParam.class))).thenReturn(new MockTransformResult()); Mockito.when(engine.getSparkContext()).thenReturn(Mockito.mock(SparkContext.class)); // Test executing a request final TransformRequest request = new TransformRequest(); request.setScript("sqlContext.range(1,10)"); final TransformService service = new TransformService(TransformScript.class, engine, sparkContextService, new MockTransformJobTracker()); final TransformResponse response = service.execute(request); Assert.assertEquals(TransformResponse.Status.SUCCESS, response.getStatus()); // Test eval arguments final ArgumentCaptor<String> evalScript = ArgumentCaptor.forClass(String.class); final ArgumentCaptor<List> evalBindings = ArgumentCaptor.forClass(List.class); Mockito.verify(engine).eval(evalScript.capture(), evalBindings.capture()); final String expectedScript = IOUtils.toString(getClass().getResourceAsStream("transform-service-script1.scala"), "UTF-8"); Assert.assertEquals(expectedScript, evalScript.getValue()); final List<NamedParam> bindings = evalBindings.getValue(); Assert.assertEquals(3, bindings.size()); Assert.assertEquals("profiler", bindings.get(0).name()); Assert.assertEquals("com.thinkbiganalytics.spark.dataprofiler.Profiler", bindings.get(0).tpe()); Assert.assertNull(bindings.get(0).value()); Assert.assertEquals("sparkContextService", bindings.get(1).name()); Assert.assertEquals("com.thinkbiganalytics.spark.SparkContextService", bindings.get(1).tpe()); Assert.assertEquals(sparkContextService, bindings.get(1).value()); Assert.assertEquals("tableName", bindings.get(2).name()); Assert.assertEquals("String", bindings.get(2).tpe()); Assert.assertTrue(((String) bindings.get(2).value()).matches("^[0-9a-f]{32}$")); } /** * Verify executing a transformation request with a data source provider factory. */ @Test @SuppressWarnings("unchecked") public void executeWithDatasourceProviderFactory() throws Exception { // Mock Spark context service final SparkContextService sparkContextService = Mockito.mock(SparkContextService.class); // Mock Spark script engine final SparkScriptEngine engine = Mockito.mock(SparkScriptEngine.class); Mockito.when(engine.eval(Mockito.anyString(), Mockito.anyListOf(NamedParam.class))).thenReturn(new MockTransformResult()); Mockito.when(engine.getSparkContext()).thenReturn(Mockito.mock(SparkContext.class)); // Mock data source provider factory final DatasourceProvider datasourceProvider = Mockito.mock(DatasourceProvider.class); final DatasourceProviderFactory datasourceProviderFactory = Mockito.mock(DatasourceProviderFactory.class); Mockito.when(datasourceProviderFactory.getDatasourceProvider(Mockito.anyCollectionOf(Datasource.class))).thenReturn(datasourceProvider); // Mock profiler final Profiler profiler = Mockito.mock(Profiler.class); // Test executing a request final TransformRequest request = new TransformRequest(); request.setDatasources(Collections.singletonList(Mockito.mock(Datasource.class))); request.setScript("sqlContext.range(1,10)"); final TransformService service = new TransformService(TransformScript.class, engine, sparkContextService, new MockTransformJobTracker()); service.setDatasourceProviderFactory(datasourceProviderFactory); service.setProfiler(profiler); final TransformResponse response = service.execute(request); Assert.assertEquals(TransformResponse.Status.SUCCESS, response.getStatus()); // Test eval arguments final ArgumentCaptor<String> evalScript = ArgumentCaptor.forClass(String.class); final ArgumentCaptor<List> evalBindings = ArgumentCaptor.forClass(List.class); Mockito.verify(engine).eval(evalScript.capture(), evalBindings.capture()); final String expectedScript = IOUtils.toString(getClass().getResourceAsStream("transform-service-script1.scala"), "UTF-8"); Assert.assertEquals(expectedScript, evalScript.getValue()); final List<NamedParam> bindings = evalBindings.getValue(); Assert.assertEquals(4, bindings.size()); Assert.assertEquals("profiler", bindings.get(0).name()); Assert.assertEquals("com.thinkbiganalytics.spark.dataprofiler.Profiler", bindings.get(0).tpe()); Assert.assertEquals(profiler, bindings.get(0).value()); Assert.assertEquals("sparkContextService", bindings.get(1).name()); Assert.assertEquals("com.thinkbiganalytics.spark.SparkContextService", bindings.get(1).tpe()); Assert.assertEquals(sparkContextService, bindings.get(1).value()); Assert.assertEquals("tableName", bindings.get(2).name()); Assert.assertEquals("String", bindings.get(2).tpe()); Assert.assertTrue(((String) bindings.get(2).value()).matches("^[0-9a-f]{32}$")); Assert.assertEquals("datasourceProvider", bindings.get(3).name()); Assert.assertEquals("com.thinkbiganalytics.spark.shell.DatasourceProvider[org.apache.spark.sql.DataFrame]", bindings.get(3).tpe()); Assert.assertEquals(datasourceProvider, bindings.get(3).value()); } /** * Verify converting a transformation request to a Scala script. */ @Test public void toScript() throws Exception { // Build the request final TransformRequest request = new TransformRequest(); request.setScript("sqlContext.range(1,10)"); // Test converting request to script final TransformService service = new TransformService(TransformScript.class, Mockito.mock(SparkScriptEngine.class), Mockito.mock(SparkContextService.class), Mockito.mock(TransformJobTracker.class)); final String expected = IOUtils.toString(getClass().getResourceAsStream("transform-service-script1.scala"), "UTF-8"); Assert.assertEquals(expected, service.toScript(request)); } /** * Verify converting a transformation request with a parent to a Scala script. */ @Test public void toScriptWithParent() throws Exception { // Build the request final TransformRequest.Parent parent = new TransformRequest.Parent(); parent.setScript("sqlContext.range(1,10)"); parent.setTable("parent_table"); final TransformRequest request = new TransformRequest(); request.setParent(parent); request.setScript("parent.withColumn(functions.expr(\"id+1\")"); // Test converting request to script final TransformService service = new TransformService(TransformScript.class, Mockito.mock(SparkScriptEngine.class), Mockito.mock(SparkContextService.class), Mockito.mock(TransformJobTracker.class)); final String expected = IOUtils.toString(getClass().getResourceAsStream("transform-service-script2.scala"), "UTF-8"); Assert.assertEquals(expected, service.toScript(request)); } /** * A mock implementation of {@link TransformJobTracker} for testing. */ private static class MockTransformJobTracker extends TransformJobTracker { /** * Constructs a {@code MockTransformJobTracker}. */ MockTransformJobTracker() { super(Thread.currentThread().getContextClassLoader()); } @Override public void addSparkListener(@Nonnull SparkScriptEngine engine) { // ignored } } /** * A mock result from a {@link TransformScript} for testing. */ private static class MockTransformResult implements Callable<TransformResponse> { @Override public TransformResponse call() throws Exception { final TransformResponse response = new TransformResponse(); response.setStatus(TransformResponse.Status.SUCCESS); return response; } } }