/*
* Copyright 2013-2014 the original author or authors.
*
* 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 org.springframework.xd.dirt.stream.completionnoscan;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.xd.rest.domain.CompletionKind.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xd.dirt.module.ModuleDependencyRepository;
import org.springframework.xd.dirt.module.ModuleRegistry;
import org.springframework.xd.dirt.module.ResourceModuleRegistry;
import org.springframework.xd.dirt.module.store.ZooKeeperModuleDependencyRepository;
import org.springframework.xd.dirt.stream.XDParser;
import org.springframework.xd.dirt.stream.XDStreamParser;
import org.springframework.xd.dirt.stream.completion.CompletionProvider;
import org.springframework.xd.dirt.zookeeper.EmbeddedZooKeeper;
import org.springframework.xd.dirt.zookeeper.ZooKeeperConnection;
import org.springframework.xd.module.ModuleDefinition;
import org.springframework.xd.module.ModuleType;
import org.springframework.xd.module.options.DefaultModuleOptionsMetadataResolver;
import org.springframework.xd.module.options.ModuleOptionsMetadataResolver;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CompletionProviderTests.Config.class})
public class CompletionProviderTests {
@Autowired
private CompletionProvider completionProvider;
@Autowired
private ModuleRegistry moduleRegistry;
@Test
// <TAB> => file,http,etc
public void testEmptyStartShouldProposeSourceModules() {
List<String> completions = completionProvider.complete(stream, "", 1);
assertThat(new HashSet<>(completions), equalTo(new HashSet<>(namesOfModulesWithType(ModuleType.source))));
}
@Test
// fi<TAB> => file
public void testUnfinishedModuleNameShouldReturnCompletions() {
List<String> completions = completionProvider.complete(stream, "fi", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file")));
completions = completionProvider.complete(stream, "file | tr", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | transform")));
}
@Test
// file | filter<TAB> => file | filter | foo, etc
public void testValidSubStreamDefinitionShouldReturnPipe() {
List<String> completions = completionProvider.complete(stream, "file | filter", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter |")));
}
@Test
// file | filter<TAB> => file | filter --foo=, etc
public void testValidSubStreamDefinitionShouldReturnModuleOptions() {
List<String> completions = completionProvider.complete(stream, "file | filter", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --script=")));
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --expression=")));
}
@Test
// file | filter -<TAB> => file | filter --foo,etc
public void testOneDashShouldReturnTwoDashes() {
List<String> completions = completionProvider.complete(stream, "file | filter -", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --script=")));
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --expression=")));
assertThat(new HashSet<>(completions), not(hasItem(startsWith("file | filter |"))));
}
@Test
// file | filter --<TAB> => file | filter --foo,etc
public void testTwoDashesShouldReturnOptions() {
List<String> completions = completionProvider.complete(stream, "file | filter --", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --script=")));
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter --expression=")));
assertThat(new HashSet<>(completions), not(hasItem(startsWith("file | filter |"))));
}
@Test
// file |<TAB> => file | foo,etc
public void testDanglingPipeShouldReturnExtraModulesOnlyOneModule() {
List<String> completions = completionProvider.complete(stream, "file |", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter")));
assertThat(new HashSet<>(completions), hasItem(startsWith("file | script")));
}
@Test
// file |<TAB> => file | filter | foo,etc
// Adding a specified test for this because of the way the parser guesses module type
// may currently interfere if there is only one module (thinks it's a job)
public void testDanglingPipeShouldReturnExtraModulesMoreThanOneModule() {
List<String> completions = completionProvider.complete(stream, "file | filter |", 1);
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter | filter")));
assertThat(new HashSet<>(completions), hasItem(startsWith("file | filter | script")));
}
@Test
// file --p<TAB> => file --preventDuplicates=, file --pattern=
public void testUnfinishedOptionNameShouldComplete() {
List<String> completions = completionProvider.complete(stream, "file | filter | jdbc --url=foo --ini", 1);
assertThat(new HashSet<>(completions),
hasItem(startsWith("file | filter | jdbc --url=foo --initializerScript")));
assertThat(new HashSet<>(completions),
hasItem(startsWith("file | filter | jdbc --url=foo --initializeDatabase")));
assertThat(new HashSet<>(completions),
not(hasItem(startsWith("file | filter | jdbc --url=foo --driverClassName"))));
completions = completionProvider.complete(stream, "file | filter --ex", 1);
assertThat(new HashSet<>(completions), not(hasItem(startsWith("file | filter |"))));
}
@Test
// file | counter --name=foo --inputType=bar<TAB> => we're done
public void testSinkWithAllOptionsSetCantGoFurther() {
List<String> completions = completionProvider.complete(stream,
"file | log --expression=payload --level=INFO --name=foo --inputType=text/plain", 1);
assertThat(completions, hasSize(0));
}
@Test
// file | counter --name=<TAB> => nothing
public void testInGenericOptionValueCantProposeAnything() {
List<String> completions = completionProvider.complete(stream,
"file | counter --name=", 1);
assertThat(completions, hasSize(0));
}
@Test
public void testJobNameCompletions() {
List<String> completions = completionProvider.complete(job, "", 1);
assertThat(completions, hasItem(startsWith("hdfs")));
assertThat(completions, not(hasItem(startsWith("gemfire-cq"))));
completions = completionProvider.complete(job, "hdf", 1);
assertThat(completions, hasItem(startsWith("hdfs")));
}
@Test
public void testJobOptionsCompletions() {
List<String> completions = completionProvider.complete(job, "filejdbc --", 1);
assertThat(completions, hasItem(startsWith("filejdbc --resources")));
}
@Test
public void testComposedModuleCompletions() {
List<String> completions = completionProvider.complete(module, "", 1);
assertThat(completions, hasItem(startsWith("http")));
assertThat(completions, hasItem(startsWith("transform")));
assertThat(completions, not(hasItem(startsWith("splunk"))));
completions = completionProvider.complete(module, "t", 1);
assertThat(completions, hasItem(startsWith("tcp")));
assertThat(completions, hasItem(startsWith("transform")));
completions = completionProvider.complete(module, "tcp | s", 1);
assertThat(completions, hasItem(startsWith("tcp | splitter")));
assertThat(completions, hasItem(startsWith("tcp | splunk")));
assertThat(completions, not(hasItem(startsWith("tcp | syslog-tcp"))));
completions = completionProvider.complete(module, "transform | s", 1);
assertThat(completions, hasItem(startsWith("transform | splitter")));
assertThat(completions, hasItem(startsWith("transform | splunk")));
assertThat(completions, not(hasItem(startsWith("transform | syslog-tcp"))));
}
@Test
// queue:foo > <TAB> ==> add module names
public void testXD1706() {
List<String> completions = completionProvider.complete(stream, "queue:foo > ", 1);
assertThat(completions, hasItem(startsWith("queue:foo > http-client")));
assertThat(completions, hasItem(startsWith("queue:foo > splunk")));
assertThat(completions, not(hasItem(startsWith("queue:foo > twittersearch"))));
}
@Test
// tap:stream:foo > <TAB> ==> add module names
public void testXD1706Variant() {
List<String> completions = completionProvider.complete(stream, "tap:stream:foo > ", 1);
assertThat(completions, hasItem(startsWith("tap:stream:foo > http-client")));
assertThat(completions, hasItem(startsWith("tap:stream:foo > splunk")));
assertThat(completions, not(hasItem(startsWith("tap:stream:foo > twittersearch"))));
}
@Test
public void testCompleteAlreadyStartedNameAfterChannel() {
List<String> completions = completionProvider.complete(stream, "queue:foo > s", 1);
// XD-1830: Currently does not support adding processors due do the way
// module type guessing works
//assertThat(completions, hasItem(startsWith("queue:foo > splitter")));
assertThat(completions, hasItem(startsWith("queue:foo > splunk")));
assertThat(completions, not(hasItem(startsWith("queue:foo > syslog"))));
}
@Test
public void testHiddenOptionNames() {
List<String> completions = completionProvider.complete(stream, "http | jdbc ", 1);
assertThat(completions, not(hasItem(startsWith("http | jdbc --maxIdle"))));
completions = completionProvider.complete(stream, "http | jdbc ", 2);
assertThat(completions, hasItem(startsWith("http | jdbc --maxIdle")));
}
private List<String> namesOfModulesWithType(ModuleType type) {
List<ModuleDefinition> mods = moduleRegistry.findDefinitions(type);
List<String> result = new ArrayList<String>();
for (ModuleDefinition mod : mods) {
result.add(mod.getName());
}
return result;
}
@Configuration
@ComponentScan(basePackageClasses = CompletionProvider.class)
public static class Config {
@Bean
public ModuleRegistry moduleRegistry() {
return new ResourceModuleRegistry("file:../modules");
}
@Bean
public ModuleDependencyRepository moduleDependencyRepository() {
return new ZooKeeperModuleDependencyRepository(zooKeeperConnection());
}
@Bean
public XDParser parser() {
return new XDStreamParser(moduleRegistry(), moduleOptionsMetadataResolver());
}
@Bean
public ModuleOptionsMetadataResolver moduleOptionsMetadataResolver() {
return new DefaultModuleOptionsMetadataResolver();
}
@Bean
public ZooKeeperConnection zooKeeperConnection() {
return new ZooKeeperConnection("localhost:" + embeddedZooKeeper().getClientPort());
}
@Bean
public EmbeddedZooKeeper embeddedZooKeeper() {
return new EmbeddedZooKeeper();
}
}
}