mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-04 08:18:04 +02:00
CLI Support for repository actions (#1987)
To make SCM-Manager more accessible and to make it easier using scripts against the server, we created a command line interface. This command line interface can be used to perform the default actions like create, modify and delete repositories. It is also very flexible and can be extended by plugins. The CLI already supports internationalization, help texts, input validation, loose and table-like templates and nested subcommands. Check the cli guidelines to learn how add new cli commands. Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com>
This commit is contained in:
171
scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java
Normal file
171
scm-webapp/src/test/java/sonia/scm/cli/CliProcessorTest.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CliProcessorTest {
|
||||
|
||||
@Mock
|
||||
private CommandRegistry registry;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CliContext context;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setDefaultLocale() {
|
||||
when(context.getLocale()).thenReturn(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExecutePingCommand() {
|
||||
when(registry.createCommandTree()).thenReturn(Collections.singleton(new RegisteredCommandNode("ping", PingCommand.class)));
|
||||
Injector injector = Guice.createInjector();
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector);
|
||||
|
||||
cliProcessor.execute(context, "ping");
|
||||
|
||||
verify(context.getStdout()).println("PONG");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExecutePingCommandWithExitCode0() {
|
||||
when(registry.createCommandTree()).thenReturn(Collections.singleton(new RegisteredCommandNode("ping", PingCommand.class)));
|
||||
Injector injector = Guice.createInjector();
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector);
|
||||
|
||||
int exitCode = cliProcessor.execute(context, "ping");
|
||||
|
||||
assertThat(exitCode).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintCommandOne() {
|
||||
String result = executeHierachyCommands("--help");
|
||||
|
||||
assertThat(result).contains("Commands:\n" +
|
||||
" one");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintCommandTwo() {
|
||||
String result = executeHierachyCommands("one","--help");
|
||||
|
||||
assertThat(result).contains("Commands:\n" +
|
||||
" two");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintCommandThree() {
|
||||
String result = executeHierachyCommands("one", "two","--help");
|
||||
|
||||
assertThat(result).contains("Commands:\n" +
|
||||
" three");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String executeHierachyCommands(String... args) {
|
||||
RegisteredCommandNode one = new RegisteredCommandNode("one", RootCommand.class);
|
||||
RegisteredCommandNode two = new RegisteredCommandNode("two", SubCommand.class);
|
||||
RegisteredCommandNode three = new RegisteredCommandNode("three", SubSubCommand.class);
|
||||
two.getChildren().add(three);
|
||||
one.getChildren().add(two);
|
||||
|
||||
when(registry.createCommandTree()).thenReturn(Collections.singleton(one));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
when(context.getStdout()).thenReturn(new PrintWriter(baos));
|
||||
|
||||
Injector injector = Guice.createInjector();
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector);
|
||||
|
||||
cliProcessor.execute(context, args);
|
||||
return baos.toString();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void shouldUseResourceBundleFromAnnotationWithContextLocale() {
|
||||
when(context.getLocale()).thenReturn(Locale.GERMAN);
|
||||
|
||||
String helpForThree = executeHierachyCommands("one", "two", "three", "--help");
|
||||
|
||||
assertThat(helpForThree).contains("Dies ist meine App.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseDefaultWithoutResourceBundle() {
|
||||
when(context.getLocale()).thenReturn(Locale.GERMAN);
|
||||
|
||||
String helpForTwo = executeHierachyCommands("one", "two", "--help");
|
||||
|
||||
assertThat(helpForTwo).contains("Dies ist meine App.");
|
||||
}
|
||||
|
||||
@CommandLine.Command(name = "one")
|
||||
static class RootCommand implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@CommandLine.Command(name = "two")
|
||||
static class SubCommand implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@CommandLine.Command(name = "three", resourceBundle = "sonia.scm.cli.test")
|
||||
static class SubSubCommand implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CommandRegistryTest {
|
||||
|
||||
@Mock
|
||||
private RegisteredCommandCollector commandCollector;
|
||||
@InjectMocks
|
||||
private CommandRegistry registry;
|
||||
|
||||
@Test
|
||||
void shouldCreateTreeWithOnlyRootNodes() {
|
||||
mockCommands(rc(Object.class), rc(String.class), rc(Integer.class));
|
||||
|
||||
Set<RegisteredCommandNode> commandTree = registry.createCommandTree();
|
||||
assertContainsCommands(commandTree, Object.class, String.class, Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateTreeWithParents() {
|
||||
mockCommands(rc(Object.class), rc(String.class, Object.class), rc(Integer.class, Object.class));
|
||||
|
||||
Set<RegisteredCommandNode> commandTree = registry.createCommandTree();
|
||||
|
||||
assertContainsCommands(commandTree, Object.class);
|
||||
assertContainsCommands(commandTree.iterator().next().getChildren(), Integer.class, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateTreeWithParentsSecondLevel() {
|
||||
mockCommands(rc(Object.class), rc(String.class, Object.class), rc(Integer.class, String.class));
|
||||
|
||||
Set<RegisteredCommandNode> commandTree = registry.createCommandTree();
|
||||
|
||||
assertContainsCommands(commandTree, Object.class);
|
||||
RegisteredCommandNode rootNode = commandTree.iterator().next();
|
||||
assertContainsCommands(rootNode.getChildren(), String.class);
|
||||
assertContainsCommands(rootNode.getChildren().get(0).getChildren(), Integer.class);
|
||||
}
|
||||
|
||||
private void mockCommands(RegisteredCommand... commands) {
|
||||
when(commandCollector.collect()).thenReturn(ImmutableSet.copyOf(commands));
|
||||
}
|
||||
|
||||
private RegisteredCommand rc(Class<?> command) {
|
||||
return rc(command, null);
|
||||
}
|
||||
|
||||
private RegisteredCommand rc(Class<?> command, Class<?> parent) {
|
||||
return new RegisteredCommand(command.getSimpleName(), command, parent);
|
||||
}
|
||||
|
||||
private void assertContainsCommands(Collection<RegisteredCommandNode> nodes, Class... expected) {
|
||||
assertThat(nodes).map(RegisteredCommandNode::getCommand).contains(expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class JsonStreamingCliContextTest {
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
void shouldPrintJsonOnStdout() throws IOException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try (JsonStreamingCliContext jsonStreamingCliContext = new JsonStreamingCliContext(Locale.ENGLISH, bais, baos)) {
|
||||
jsonStreamingCliContext.getStdout().print("Hello");
|
||||
}
|
||||
|
||||
JsonNode json = mapper.readTree(new ByteArrayInputStream(baos.toByteArray()));
|
||||
assertThat(json.isArray()).isTrue();
|
||||
assertThat(json.get(0).get("out").asText()).isEqualTo("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintJsonOnStdoutAndStderr() throws IOException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try (JsonStreamingCliContext jsonStreamingCliContext = new JsonStreamingCliContext(Locale.ENGLISH, bais, baos)) {
|
||||
jsonStreamingCliContext.getStdout().print("Hello");
|
||||
jsonStreamingCliContext.getStderr().print("Error 1: Failed");
|
||||
jsonStreamingCliContext.getStdout().print(" World");
|
||||
}
|
||||
|
||||
JsonNode json = mapper.readTree(new ByteArrayInputStream(baos.toByteArray()));
|
||||
assertThat(json.isArray()).isTrue();
|
||||
assertThat(json.get(0).get("out").asText()).isEqualTo("Hello");
|
||||
assertThat(json.get(1).get("err").asText()).isEqualTo("Error 1: Failed");
|
||||
assertThat(json.get(2).get("out").asText()).isEqualTo(" World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnExitCode() throws IOException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try (JsonStreamingCliContext jsonStreamingCliContext = new JsonStreamingCliContext(Locale.ENGLISH, bais, baos)) {
|
||||
jsonStreamingCliContext.getStdout().print("Hello");
|
||||
jsonStreamingCliContext.writeExit(1);
|
||||
}
|
||||
|
||||
JsonNode json = mapper.readTree(new ByteArrayInputStream(baos.toByteArray()));
|
||||
assertThat(json.isArray()).isTrue();
|
||||
assertThat(json.get(0).get("out").asText()).isEqualTo("Hello");
|
||||
assertThat(json.get(1).get("exit").asInt()).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.plugin.InstalledPlugin;
|
||||
import sonia.scm.plugin.InstalledPluginDescriptor;
|
||||
import sonia.scm.plugin.NamedClassElement;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.ScmModule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RegisteredCommandCollectorTest {
|
||||
|
||||
@Mock
|
||||
private PluginLoader pluginLoader;
|
||||
|
||||
@InjectMocks
|
||||
private RegisteredCommandCollector commandCollector;
|
||||
|
||||
@Test
|
||||
void shouldCollectCommandsFromModulesAndPlugins() {
|
||||
ScmModule module = mock(ScmModule.class);
|
||||
when(pluginLoader.getInstalledModules()).thenReturn(ImmutableList.of(module));
|
||||
|
||||
when(module.getCliCommands()).thenReturn(ImmutableList.of(new NamedClassElement("moduleCommand", ModuleCommand.class.getName())));
|
||||
InstalledPlugin installedPlugin = mock(InstalledPlugin.class);
|
||||
InstalledPluginDescriptor descriptor = mock(InstalledPluginDescriptor.class);
|
||||
when(pluginLoader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedPlugin));
|
||||
when(installedPlugin.getDescriptor()).thenReturn(descriptor);
|
||||
when(descriptor.getCliCommands()).thenReturn(ImmutableList.of(new NamedClassElement("subCommand", SubCommand.class.getName())));
|
||||
when(pluginLoader.getUberClassLoader()).thenReturn(RegisteredCommandCollectorTest.class.getClassLoader());
|
||||
|
||||
Set<RegisteredCommand> commands = commandCollector.collect();
|
||||
|
||||
assertThat(commands).hasSize(2);
|
||||
assertThat(commands)
|
||||
.map(RegisteredCommand::getName)
|
||||
.containsExactlyInAnyOrder("subCommand", "moduleCommand");
|
||||
|
||||
List<Class<?>> commandClasses = commands.stream().map(RegisteredCommand::getCommand).collect(Collectors.toList());
|
||||
assertThat(commandClasses).containsExactlyInAnyOrder(SubCommand.class, ModuleCommand.class);
|
||||
}
|
||||
|
||||
static class ParentCommand {
|
||||
|
||||
}
|
||||
|
||||
@sonia.scm.cli.ParentCommand(value = ParentCommand.class)
|
||||
static class SubCommand {
|
||||
|
||||
}
|
||||
|
||||
@sonia.scm.cli.ParentCommand(value = ParentCommand.class)
|
||||
static class ModuleCommand {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryInitializer;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryCreateCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager manager;
|
||||
@Mock
|
||||
private RepositoryInitializer initializer;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private CommandValidator commandValidator;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryCreateCommand command;
|
||||
|
||||
@Test
|
||||
void shouldValidate() {
|
||||
command.setType("git");
|
||||
command.setRepository("test/repo");
|
||||
command.run();
|
||||
|
||||
verify(commandValidator).validate();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateRepoWithoutInit() {
|
||||
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
|
||||
command.setType(heartOfGold.getType());
|
||||
command.setRepository(heartOfGold.getNamespaceAndName().toString());
|
||||
command.run();
|
||||
|
||||
verify(manager).create(argThat(repository -> {
|
||||
assertThat(repository.getType()).isEqualTo(heartOfGold.getType());
|
||||
return true;
|
||||
}));
|
||||
verify(initializer, never()).initialize(eq(heartOfGold), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateRepoWithInit() {
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
when(manager.create(any())).thenReturn(puzzle);
|
||||
command.setType(puzzle.getType());
|
||||
command.setRepository(puzzle.getNamespaceAndName().toString());
|
||||
command.setInit(true);
|
||||
command.run();
|
||||
|
||||
verify(initializer).initialize(eq(puzzle), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderTemplateAfterCreation() {
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
when(manager.create(any())).thenReturn(puzzle);
|
||||
|
||||
command.setType(puzzle.getType());
|
||||
command.setRepository(puzzle.getNamespaceAndName().toString());
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).render(puzzle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.CliContext;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryDeleteCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private RepositoryManager manager;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryDeleteCommand command;
|
||||
|
||||
@Test
|
||||
void shouldRenderPromptWithoutYesFlag() {
|
||||
command.setRepository("test/repo");
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderToStderr(any(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExitOnInvalidInput() {
|
||||
command.setRepository("test");
|
||||
command.setShouldDelete(true);
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteRepository() {
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
when(manager.get(new NamespaceAndName("test", "r"))).thenReturn(puzzle);
|
||||
command.setRepository("test/r");
|
||||
command.setShouldDelete(true);
|
||||
command.run();
|
||||
|
||||
verify(manager).delete(puzzle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
import sonia.scm.repository.cli.RepositoryGetCommand;
|
||||
import sonia.scm.repository.cli.RepositoryTemplateRenderer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryGetCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryGetCommand command;
|
||||
|
||||
@Test
|
||||
void shouldRenderNotFoundError() {
|
||||
String repo = "test/repo";
|
||||
|
||||
when(repositoryManager.get(new NamespaceAndName("test", "repo"))).thenReturn(null);
|
||||
|
||||
command.setRepository(repo);
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderInvalidInputError() {
|
||||
String repo = "repo";
|
||||
|
||||
command.setRepository(repo);
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderTemplateToStdout() {
|
||||
String repo = "test/repo";
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
when(repositoryManager.get(new NamespaceAndName("test", "repo"))).thenReturn(puzzle);
|
||||
|
||||
command.setRepository(repo);
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).render(puzzle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.Table;
|
||||
import sonia.scm.cli.TemplateRenderer;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryListCommandTest {
|
||||
|
||||
@Mock
|
||||
private TemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private RepositoryManager manager;
|
||||
@Mock
|
||||
private RepositoryToRepositoryCommandDtoMapper mapper;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryListCommand command;
|
||||
|
||||
@Test
|
||||
void shouldReturnShortTemplate() {
|
||||
command.setShortTemplate(true);
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer, never()).createTable();
|
||||
verify(templateRenderer).renderToStdout(any(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTableTemplate() {
|
||||
Table table = mock(Table.class);
|
||||
when(templateRenderer.createTable()).thenReturn(table);
|
||||
|
||||
ArgumentCaptor<Map<String, Object>> mapCaptor = ArgumentCaptor.forClass(Map.class);
|
||||
ArgumentCaptor<String> templateCaptor = ArgumentCaptor.forClass(String.class);
|
||||
doNothing().when(templateRenderer).renderToStdout(templateCaptor.capture(), mapCaptor.capture());
|
||||
command.run();
|
||||
|
||||
Map<String, Object> map = mapCaptor.getValue();
|
||||
assertThat(map).hasSize(2);
|
||||
assertThat(map.get("rows")).isInstanceOf(Table.class);
|
||||
assertThat(map.get("repos")).isInstanceOf(List.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryModifyCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private CommandValidator validator;
|
||||
@Mock
|
||||
private RepositoryManager manager;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryModifyCommand command;
|
||||
|
||||
@Test
|
||||
void shouldValidateParameters() {
|
||||
command.setRepository("test/repo");
|
||||
command.run();
|
||||
|
||||
verify(validator).validate();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderInvalidInputError() {
|
||||
command.setRepository("test");
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderNotFoundError() {
|
||||
when(manager.get(new NamespaceAndName("test", "repo"))).thenReturn(null);
|
||||
command.setRepository("test/repo");
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldModifyRepository() {
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
when(manager.get(new NamespaceAndName("test", "repo"))).thenReturn(puzzle);
|
||||
command.setRepository("test/repo");
|
||||
command.run();
|
||||
|
||||
verify(manager).modify(puzzle);
|
||||
verify(templateRenderer).render(puzzle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.api.ScmProtocol;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryToRepositoryCommandDtoMapperTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
@Mock
|
||||
private RepositoryService service;
|
||||
|
||||
private RepositoryToRepositoryCommandDtoMapperImpl mapper;
|
||||
|
||||
@BeforeEach
|
||||
void initMapper() {
|
||||
mapper = new RepositoryToRepositoryCommandDtoMapperImpl();
|
||||
mapper.setServiceFactory(serviceFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapAttributes() {
|
||||
Repository testRepo = RepositoryTestData.create42Puzzle();
|
||||
when(serviceFactory.create(testRepo)).thenReturn(service);
|
||||
RepositoryCommandDto dto = mapper.map(testRepo);
|
||||
|
||||
assertThat(dto.getNamespace()).isEqualTo(testRepo.getNamespace());
|
||||
assertThat(dto.getName()).isEqualTo(testRepo.getName());
|
||||
assertThat(dto.getContact()).isEqualTo(testRepo.getContact());
|
||||
assertThat(dto.getDescription()).isEqualTo(testRepo.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAppendHttpUrl() {
|
||||
ScmProtocol scmProtocol = new ScmProtocol() {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "http";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return "http://localhost:8081/scm";
|
||||
}
|
||||
};
|
||||
Repository testRepo = RepositoryTestData.create42Puzzle();
|
||||
|
||||
RepositoryService service = mock(RepositoryService.class);
|
||||
when(serviceFactory.create(testRepo)).thenReturn(service);
|
||||
when(service.getSupportedProtocols()).thenReturn(ImmutableList.of(scmProtocol).stream());
|
||||
|
||||
RepositoryCommandDto dto = mapper.map(testRepo);
|
||||
|
||||
assertThat(dto.getUrl()).isEqualTo("http://localhost:8081/scm");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DefaultValidatorProviderTest {
|
||||
|
||||
@Test
|
||||
void shouldCreateValidatorWithConstraintValidatorFactory() {
|
||||
TestingConstraintValidatorFactory constraintValidatorFactory = new TestingConstraintValidatorFactory();
|
||||
DefaultValidatorProvider provider = new DefaultValidatorProvider(constraintValidatorFactory);
|
||||
Validator validator = provider.get();
|
||||
validator.validate(new Sample("one"));
|
||||
|
||||
assertThat(constraintValidatorFactory.counter).isOne();
|
||||
}
|
||||
|
||||
private static class TestingConstraintValidatorFactory implements ConstraintValidatorFactory {
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
@Override
|
||||
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
|
||||
counter++;
|
||||
try {
|
||||
return key.getConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseInstance(ConstraintValidator<?, ?> instance) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class Sample {
|
||||
|
||||
@Size(max = 20)
|
||||
private final String value;
|
||||
|
||||
public Sample(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GuiceConstraintValidatorFactoryTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
@Test
|
||||
void shouldUseInjectorToCreateConstraintInstance() {
|
||||
Injector injector = Guice.createInjector(new RepositoryManagerModule(repositoryManager));
|
||||
GuiceConstraintValidatorFactory factory = new GuiceConstraintValidatorFactory(injector);
|
||||
RepositoryTypeConstraintValidator instance = factory.getInstance(RepositoryTypeConstraintValidator.class);
|
||||
assertThat(instance.getRepositoryManager()).isSameAs(repositoryManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
public class RepositoryManagerModule extends AbstractModule {
|
||||
|
||||
private final RepositoryManager repositoryManager;
|
||||
|
||||
public RepositoryManagerModule(RepositoryManager repositoryManager) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(RepositoryManager.class).toInstance(repositoryManager);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Constraint(validatedBy = RepositoryTypeConstraintValidator.class)
|
||||
public @interface RepositoryType {
|
||||
String message() default "Invalid type";
|
||||
Class<?>[] groups() default { };
|
||||
Class<? extends Payload>[] payload() default { };
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class RepositoryTypeConstraintValidator implements ConstraintValidator<RepositoryType, String> {
|
||||
|
||||
private final RepositoryManager repositoryManager;
|
||||
|
||||
@Inject
|
||||
public RepositoryTypeConstraintValidator(RepositoryManager repositoryManager) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
}
|
||||
|
||||
public RepositoryManager getRepositoryManager() {
|
||||
return repositoryManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
return repositoryManager.getConfiguredTypes()
|
||||
.stream().anyMatch(t -> t.getName().equalsIgnoreCase(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.validation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ValidationModuleTest {
|
||||
|
||||
@Nested
|
||||
class SimpleInjectionTests {
|
||||
|
||||
private Injector injector;
|
||||
|
||||
@BeforeEach
|
||||
void setUpInjector() {
|
||||
injector = Guice.createInjector(new ValidationModule());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInjectValidator() {
|
||||
WithValidator instance = injector.getInstance(WithValidator.class);
|
||||
assertThat(instance.validator).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInjectConstraintValidatorFactory() {
|
||||
WithConstraintValidatorFactory instance = injector.getInstance(WithConstraintValidatorFactory.class);
|
||||
assertThat(instance.constraintValidatorFactory).isInstanceOf(GuiceConstraintValidatorFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RealWorldTests {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
@Test
|
||||
void shouldValidateRepositoryTypes() {
|
||||
when(repositoryManager.getConfiguredTypes()).thenReturn(ImmutableList.of(
|
||||
new sonia.scm.repository.RepositoryType("git", "Git", Collections.emptySet()),
|
||||
new sonia.scm.repository.RepositoryType("hg", "Mercurial", Collections.emptySet())
|
||||
));
|
||||
|
||||
Injector injector = Guice.createInjector(new ValidationModule(), new RepositoryManagerModule(repositoryManager));
|
||||
|
||||
Validator validator = injector.getInstance(Validator.class);
|
||||
|
||||
Set<ConstraintViolation<Repository>> violations = validator.validate(new Repository("svn"));
|
||||
assertThat(violations).isNotEmpty();
|
||||
|
||||
violations = validator.validate(new Repository("git"));
|
||||
assertThat(violations).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class WithValidator {
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
@Inject
|
||||
public WithValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WithConstraintValidatorFactory {
|
||||
|
||||
private final ConstraintValidatorFactory constraintValidatorFactory;
|
||||
|
||||
@Inject
|
||||
public WithConstraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
|
||||
this.constraintValidatorFactory = constraintValidatorFactory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
public static class Repository {
|
||||
|
||||
@RepositoryType
|
||||
private String type;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user