/*
* Copyright 2013 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.shell.command;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.springframework.xd.shell.command.fixtures.XDMatchers.eventually;
import static org.springframework.xd.shell.command.fixtures.XDMatchers.hasContentsThat;
import java.io.IOException;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.shell.core.CommandResult;
import org.springframework.xd.shell.command.fixtures.HttpSource;
import org.springframework.xd.shell.util.Table;
import org.springframework.xd.shell.util.TableRow;
import org.springframework.xd.test.fixtures.FileSink;
/**
* Test stream commands
*
* @author Mark Pollack
* @author Kashyap Parikh
* @author Andy Clement
* @author David Turanski
* @author Ilayaperumal Gopinathan
*/
public class AbstractStreamCommandsTests extends AbstractStreamIntegrationTest {
private static final Logger logger = LoggerFactory.getLogger(AbstractStreamCommandsTests.class);
@Test
public void testStreamLifecycleForTickTock() throws InterruptedException {
logger.info("Starting Stream Test for TickTock");
String streamName = generateStreamName();
stream().create(streamName, "time | log");
stream().undeploy(streamName);
}
@Test
public void testStreamCreateDuplicate() throws InterruptedException {
logger.info("Create tictock stream");
String streamName = generateStreamName();
String streamDefinition = "time | log";
stream().create(streamName, streamDefinition);
CommandResult cr = getShell().executeCommand(
"stream create --definition \"" + streamDefinition + "\" --name " + streamName);
assertTrue("Failure. CommandResult = " + cr.toString(), !cr.isSuccess());
assertTrue("Failure. CommandResult = " + cr.toString(),
cr.getException().getMessage().contains("There is already a stream named '" + streamName + "'"));
}
@Test
public void testCreatingTapWithSameNameAsExistingStream_xd299() {
String streamName = generateStreamName();
CommandResult cr = getShell().executeCommand(
"stream create --name " + streamName + " --definition \"" + getTapName(streamName) + " > counter\"");
assertTrue("Failure. CommandResult = " + cr.toString(), !cr.isSuccess());
assertTrue("Failure. CommandResult = " + cr.toString(),
cr.getException().getMessage().contains("unrecognized stream reference '" + streamName + "'"));
}
@Test
public void testStreamDestroyMissing() {
logger.info("Destroy a stream that doesn't exist");
String streamName = generateStreamName();
CommandResult cr = getShell().executeCommand("stream destroy --name " + streamName);
assertTrue("Failure. CommandResult = " + cr.toString(), !cr.isSuccess());
assertTrue("Failure. CommandResult = " + cr.toString(),
cr.getException().getMessage().contains("There is no stream definition named '" + streamName + "'"));
}
//XD-3346
@Test
public void testStreamDestroyAllWithForce() {
logger.info("Destroy all streams");
CommandResult cr = getShell().executeCommand("stream all destroy --force");
assertThat(cr.isSuccess(), is(true));
}
@Test
public void testStreamCreateDuplicateWithDeployFalse() {
logger.info("Create 2 tictok streams with --deploy = false");
String streamName = generateStreamName();
String streamDefinition = "time | log";
stream().createDontDeploy(streamName, streamDefinition);
CommandResult cr = getShell().executeCommand(
"stream create --definition \"" + streamDefinition + "\" --name " + streamName + " --deploy false");
assertTrue("Failure. CommandResult = " + cr.toString(), !cr.isSuccess());
assertTrue("Failure. CommandResult = " + cr.toString(),
cr.getException().getMessage().contains("There is already a stream named '" + streamName + "'"));
stream().verifyExists(streamName, streamDefinition, false);
}
@Test
public void testStreamDeployUndeployFlow() {
logger.info("Create tictok stream");
String streamName = generateStreamName();
String streamDefinition = "time | log";
stream().createDontDeploy(streamName, streamDefinition);
stream().deploy(streamName);
stream().verifyExists(streamName, streamDefinition, true);
stream().undeploy(streamName);
stream().verifyExists(streamName, streamDefinition, false);
stream().deploy(streamName);
stream().verifyExists(streamName, streamDefinition, true);
}
@Test
public void testNamedChannelWithNoConsumerShouldBuffer() {
String streamName = generateStreamName();
logger.info("Create " + streamName + " stream");
HttpSource source = newHttpSource();
stream().create(streamName, "%s > %s", source, generateQueueName());
source.postData("blahblah");
}
@Test
public void testNamedChannelsLinkingSourceAndSink() {
HttpSource source = newHttpSource();
String queue = generateQueueName();
stream().create(generateStreamName(), "%s > %s", source, queue);
stream().create(generateStreamName(), "%s > transform --expression=payload.toUpperCase() | log", queue);
source.postData("blahblah");
}
/**
* Test a stream that is simply one processor module connecting two named channels.
*/
@Test
public void testProcessorLinkingChannels() throws Exception {
FileSink sink = newFileSink().binary(true);
HttpSource source = newHttpSource(9314);
String streamName0 = generateStreamName();
String streamName1 = generateStreamName();
String streamName2 = generateStreamName();
String queue1 = generateQueueName();
String queue2 = generateQueueName();
stream().create(streamName0, "%s > %s", source, queue1);
stream().create(streamName1, "%s > transform --expression=payload.toUpperCase() > %s", queue1, queue2);
stream().create(streamName2, "%s > %s", queue2, sink);
source.postData("blahblah");
assertThat(String.format("Assert failed for streams %s, %s, %s", streamName0, streamName1, streamName2),
sink, eventually(hasContentsThat(equalTo("BLAHBLAH"))));
}
@Test
public void testTappingModulesVariations() throws IOException {
// Note: this test is using a regular sink, not a named channel sink
HttpSource httpSource = newHttpSource();
FileSink sink = newFileSink().binary(true);
FileSink tapsink3 = newFileSink().binary(true);
FileSink tapsink5 = newFileSink().binary(true);
FileSink tapsink6 = newFileSink().binary(true);
String streamName = generateStreamName();
stream().create(streamName, "%s | transform --expression=payload.toUpperCase() | %s", httpSource, sink);
stream().create(generateStreamName(), "%s > transform --expression=payload.replaceAll('A','.') | %s",
getTapName(streamName), tapsink3);
stream().create(generateStreamName(),
"%s.transform > transform --expression=payload.replaceAll('A','.') | %s", getTapName(streamName),
tapsink5);
stream().create(generateStreamName(),
"%s.0 > transform --expression=payload.replaceAll('A','.') | %s", getTapName(streamName), tapsink6);
// use the tap channel as a sink. Not very useful currently but will be once we allow users to create pub/sub
// channels
// stream().create(getRandomStreamName(), "%s > %s.transform",
// httpSource2, getTapName(streamName));
httpSource.ensureReady().postData("Dracarys!");
assertThat(sink, eventually(hasContentsThat(equalTo("DRACARYS!"))));
assertThat(tapsink3, eventually(hasContentsThat(equalTo("Dracarys!"))));
assertThat(tapsink5, eventually(hasContentsThat(equalTo("DR.C.RYS!"))));
assertThat(tapsink6, eventually(hasContentsThat(equalTo("Dracarys!"))));
// httpSource2.ensureReady().postData("TESTPLAN");
// our tap got the data and transformed appropriately
// assertThat(tapsink5, eventually(hasContentsThat(equalTo("DR.C.RYS!TESTPL.N"))));
// other tap did not get data
// assertThat(tapsink3, eventually(hasContentsThat(equalTo("Dracarys!"))));
}
@Test
public void testTappingModulesVariationsWithSinkChannel_XD629() throws IOException {
HttpSource source = newHttpSource();
FileSink sink = newFileSink().binary(true);
FileSink tapsink3 = newFileSink().binary(true);
FileSink tapsink5 = newFileSink().binary(true);
String streamName = generateStreamName();
String queue = generateQueueName();
stream().create(streamName,
"%s | transform --expression=payload.toUpperCase() | filter --expression=true > %s", source, queue);
stream().create(generateStreamName(), "%s > %s", queue, sink);
stream().create(generateStreamName(),
"%s > transform --expression=payload.replaceAll('r','.') | %s", getTapName(streamName), tapsink3);
stream().create(generateStreamName(),
"%s.filter > transform --expression=payload.replaceAll('A','.') | %s", getTapName(streamName),
tapsink5);
source.ensureReady().postData("Dracarys!");
assertThat(sink, eventually(hasContentsThat(equalTo("DRACARYS!"))));
assertThat(tapsink3, eventually(hasContentsThat(equalTo("D.aca.ys!"))));
assertThat(tapsink5, eventually(hasContentsThat(equalTo("DR.C.RYS!"))));
}
@Test
public void testUsingLabels() throws IOException {
FileSink sink1 = newFileSink().binary(true);
FileSink sink2 = newFileSink().binary(true);
HttpSource source = newHttpSource();
String streamName = generateStreamName();
stream().create(streamName, "%s | flibble: transform --expression=payload.toUpperCase() | %s", source, sink1);
stream().create(generateStreamName(),
"%s.flibble > transform --expression=payload.replaceAll('a','.') | %s", getTapName(streamName), sink2);
source.ensureReady().postData("Dracarys!");
assertThat(sink1, eventually(hasContentsThat(equalTo("DRACARYS!"))));
assertThat(sink2, eventually(hasContentsThat(equalTo("DRACARYS!"))));
}
@Test
public void testMaskingOfPasswords() throws IOException {
String streamName = generateStreamName();
stream().createDontDeploy(streamName, "http | jdbc --password=mypassword");
CommandResult cr = getShell().executeCommand("stream list");
assertTrue("Failure. CommandResult = " + cr.toString(), cr.isSuccess());
Table table = (Table) cr.getResult();
assertTrue(table.getRows().contains(
new TableRow().addValue(1, streamName).addValue(2, "http | jdbc --password=**********").addValue(3,
"undeployed")));
}
@Test
public void testNamedChannels() throws Exception {
HttpSource source1 = newHttpSource();
HttpSource source2 = newHttpSource();
HttpSource source3 = newHttpSource();
FileSink sink = newFileSink().binary(true);
String streamName0 = generateStreamName();
String streamName1 = generateStreamName();
String streamName2 = generateStreamName();
String queue = generateQueueName();
stream().create(streamName0, "%s > %s", source1, queue);
stream().create(streamName1, "%s > %s", source2, queue);
stream().create(streamName2, "%s > %s", queue, sink);
source1.ensureReady().postData("Dracarys!");
source2.ensureReady().postData("testing");
assertThat(String.format("Assert failed for streams %s, %s, %s", streamName0, streamName1, streamName2),
sink, eventually(hasContentsThat(equalTo("Dracarys!testing"))));
stream().destroyStream(streamName1);
source1.ensureReady().postData("stillup");
assertThat(sink, eventually(hasContentsThat(equalTo("Dracarys!testingstillup"))));
stream().create(generateStreamName(), "%s > %s", source3, queue);
source3.ensureReady().postData("newstream");
assertThat(sink, eventually(hasContentsThat(equalTo("Dracarys!testingstillupnewstream"))));
}
@Test
@Ignore("Failing on CI server - Investigate")
public void testJsonPath() throws IOException {
HttpSource source = newHttpSource();
FileSink sink = newFileSink().binary(true);
stream().create(generateStreamName(),
"%s | transform --expression='#jsonPath(payload, \"$.foo.bar\")' | %s",
source, sink);
source.ensureReady().postData("{\"foo\":{\"bar\":\"123\"}}");
assertThat(sink, eventually(40, 100, hasContentsThat(equalTo("123"))));
}
@Test
public void testDestroyTap() {
HttpSource source = newHttpSource();
String streamName = generateStreamName();
String tapName = generateStreamName();
stream().create(streamName, "%s | log", source);
stream().create(tapName, "%s > %s", getTapName(streamName), generateQueueName());
stream().destroyStream(streamName);
stream().destroyStream(tapName);
}
@Test
public void testTapCreateAndReDeploy() {
HttpSource source = newHttpSource();
FileSink tapSink1 = newFileSink().binary(true);
FileSink tapSink2 = newFileSink().binary(true);
String streamName = generateStreamName();
String tapName1 = generateStreamName();
String tapName2 = generateStreamName();
stream().create(streamName, "%s | null", source);
stream().create(tapName1, "%s > %s", getTapName(streamName), tapSink1);
source.ensureReady().postData("123");
assertThat(tapSink1, eventually(hasContentsThat(equalTo("123"))));
stream().undeploy(tapName1);
stream().create(tapName2, "%s > %s", getTapName(streamName), tapSink2);
source.ensureReady().postData("234");
assertThat(tapSink2, eventually(hasContentsThat(equalTo("234"))));
}
@Test
public void testUsingPlaceholdersInsideStreamDefinition() {
String streamName = generateStreamName();
HttpSource source = newHttpSource();
FileSink sink = newFileSink().binary(true);
stream().create(streamName, "%s | transform --expression=\"'${xd.stream.name}'\" | %s", source, sink);
source.ensureReady().postData("whatever");
assertThat(sink, eventually(hasContentsThat(equalTo(streamName))));
}
}