/*
* Copyright 2013-2015 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.rest;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.xd.dirt.module.WritableModuleRegistry;
import org.springframework.xd.module.ModuleDefinition;
import org.springframework.xd.module.ModuleDefinitions;
import org.springframework.xd.module.ModuleType;
import org.springframework.xd.module.TestModuleDefinitions;
/**
* Tests REST compliance of module-related endpoints.
*
* @author Glenn Renfro
* @author Mark Fisher
* @author Gunnar Hillert
* @author Ilayaperumal Gopinathan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RestConfiguration.class, Dependencies.class})
public class ModulesControllerIntegrationTests extends AbstractControllerIntegrationTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Autowired
private WritableModuleRegistry moduleRegistry;
@Before
public void setupSuccessfulModuleComposition() throws IOException {
ModuleDefinition any = Mockito.any();
when(moduleRegistry.registerNew(any)).thenReturn(true);
File config = temporaryFolder.newFolder("config");
Properties props = new Properties();
props.setProperty("options.foo.description", "the foo property");
props.setProperty("options.foo.type", "String");
props.setProperty("options.foo.default", "yummy");
props.store(new FileOutputStream(new File(config, "foo.properties")), "module properties");
}
@Before
public void setupFindDefinitionResults() throws IOException {
ArrayList<ModuleDefinition> moduleDefinitions = new ArrayList<ModuleDefinition>();
ArrayList<ModuleDefinition> jobDefinitions = new ArrayList<ModuleDefinition>();
ArrayList<ModuleDefinition> processorDefinitions = new ArrayList<ModuleDefinition>();
ArrayList<ModuleDefinition> sinkDefinitions = new ArrayList<ModuleDefinition>();
ArrayList<ModuleDefinition> sourceDefinitions = new ArrayList<ModuleDefinition>();
for (int moduleCount = 0; moduleCount < 3; moduleCount++) {
ModuleDefinition moduleDefinition = TestModuleDefinitions.dummy("job_" + moduleCount, ModuleType.job);
moduleDefinitions.add(moduleDefinition);
jobDefinitions.add(moduleDefinition);
moduleDefinition = TestModuleDefinitions.dummy("source_" + moduleCount, ModuleType.source);
moduleDefinitions.add(moduleDefinition);
sourceDefinitions.add(moduleDefinition);
moduleDefinition = TestModuleDefinitions.dummy("sink_" + moduleCount, ModuleType.sink);
moduleDefinitions.add(moduleDefinition);
sinkDefinitions.add(moduleDefinition);
moduleDefinition = TestModuleDefinitions.dummy("processor_" + moduleCount, ModuleType.processor);
moduleDefinitions.add(moduleDefinition);
processorDefinitions.add(moduleDefinition);
}
ModuleDefinition moduleDefinition = ModuleDefinitions.simple("job_4_with_resource", ModuleType.job, "file:" + temporaryFolder.getRoot().getAbsolutePath() + "/");
moduleDefinitions.add(moduleDefinition);
jobDefinitions.add(moduleDefinition);
for (int i = 0; i < 3; i++) {
when(moduleRegistry.findDefinition("source_" + i, ModuleType.source)).thenReturn(sourceDefinitions.get(i));
when(moduleRegistry.findDefinitions("source_" + i)).thenReturn(
Collections.singletonList(sourceDefinitions.get(i)));
when(moduleRegistry.findDefinition("processor_" + i, ModuleType.processor)).thenReturn(
processorDefinitions.get(i));
when(moduleRegistry.findDefinitions("processor_" + i)).thenReturn(
Collections.singletonList(processorDefinitions.get(i)));
when(moduleRegistry.findDefinition("sink_" + i, ModuleType.sink)).thenReturn(sinkDefinitions.get(i));
when(moduleRegistry.findDefinitions("sink_" + i)).thenReturn(
Collections.singletonList(sinkDefinitions.get(i)));
}
when(moduleRegistry.findDefinitions(ModuleType.sink)).thenReturn(sinkDefinitions);
when(moduleRegistry.findDefinitions(ModuleType.source)).thenReturn(sourceDefinitions);
when(moduleRegistry.findDefinitions(ModuleType.job)).thenReturn(jobDefinitions);
when(moduleRegistry.findDefinitions(ModuleType.processor)).thenReturn(processorDefinitions);
when(moduleRegistry.findDefinitions()).thenReturn(moduleDefinitions);
when(moduleRegistry.findDefinition("job_4_with_resource", ModuleType.job)).thenReturn(moduleDefinition);
}
@Test
public void testListAll() throws Exception {
mockMvc.perform(get("/modules").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk()).andExpect(
jsonPath("$.content", Matchers.hasSize(13))).andExpect(jsonPath("$.content[0].name").value("job_0")).andExpect(
jsonPath("$.content[3].name").value("job_4_with_resource")).andExpect(
jsonPath("$.content[6].name").value("processor_2")).andExpect(
jsonPath("$.content[9].name").value("sink_2")).andExpect(
jsonPath("$.content[12].name").value("source_2"));
}
@Test
public void testListAllWithDetails() throws Exception {
mockMvc.perform(get("/modules?detailed=true").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk()).andExpect(
jsonPath("$.content", Matchers.hasSize(13))).andExpect(jsonPath("$.content[0].name").value("job_0")).andExpect(
jsonPath("$.content[3].name").value("job_4_with_resource")).andExpect(
jsonPath("$.content[3].options[0].description").value("the foo property"));
}
@Test
public void testListJob() throws Exception {
mockMvc.perform(get("/modules").param("type", "job").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk()).andExpect(
jsonPath("$.content", Matchers.hasSize(4))).andExpect(jsonPath("$.content[0].name").value("job_0")).andExpect(
jsonPath("$.content[1].name").value("job_1")).andExpect(
jsonPath("$.content[2].name").value("job_2")).andExpect(
jsonPath("$.content[3].name").value("job_4_with_resource"));
}
@Test
public void testListSource() throws Exception {
mockMvc.perform(get("/modules").param("type", "source").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk()).andExpect(
jsonPath("$.content", Matchers.hasSize(3))).andExpect(jsonPath("$.content[0].name").value("source_0")).andExpect(
jsonPath("$.content[1].name").value("source_1")).andExpect(
jsonPath("$.content[2].name").value("source_2"));
}
@Test
public void testListSink() throws Exception {
mockMvc.perform(get("/modules").param("type", "sink").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk()).andExpect(
jsonPath("$.content", Matchers.hasSize(3))).andExpect(
jsonPath("$.content[0].name").value("sink_0")).andExpect(
jsonPath("$.content[1].name").value("sink_1")).andExpect(
jsonPath("$.content[2].name").value("sink_2"));
}
@Test
public void testDisplayNonExistingJobConfiguration() throws Exception {
mockMvc.perform(get("/modules/job/not_exists/definition").accept(MediaType.APPLICATION_XML)).andExpect(
status().isNotFound());
}
@Test
public void testListBadType() throws Exception {
mockMvc.perform(get("/modules").param("type", "foo").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isInternalServerError());
}
@Test
public void saveComposedModule() throws Exception {
mockMvc.perform(post("/modules")
.param("name", "compositesink")
.param("definition", "processor_2 | sink_1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(201)).andExpect(
jsonPath("$.name").value("compositesink")).andExpect(
jsonPath("$.type").value("sink"));
}
@Test
public void testModuleInfoWithIncorrectModuleName() throws Exception {
mockMvc.perform(get("/modules/source/test")).andExpect(status().isNotFound()).andExpect(
jsonPath("$[0].message", Matchers.is("Could not find module with name 'test' and type 'source'")));
}
}