diff --git a/src/main/java/org/springframework/cli/command/RoleCommands.java b/src/main/java/org/springframework/cli/command/RoleCommands.java index d9143841..38b19d79 100644 --- a/src/main/java/org/springframework/cli/command/RoleCommands.java +++ b/src/main/java/org/springframework/cli/command/RoleCommands.java @@ -21,20 +21,15 @@ import java.nio.file.Path; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cli.SpringCliException; import org.springframework.cli.roles.RoleService; +import org.springframework.cli.util.TableUtils; import org.springframework.cli.util.TerminalMessage; import org.springframework.shell.command.annotation.Command; import org.springframework.shell.command.annotation.Option; -import org.springframework.shell.table.ArrayTableModel; -import org.springframework.shell.table.BorderStyle; import org.springframework.shell.table.Table; -import org.springframework.shell.table.TableBuilder; -import org.springframework.shell.table.TableModel; import org.springframework.util.StringUtils; @Command(command = "role", group = "Role") @@ -129,19 +124,7 @@ public void roleGet(@Option(description = "Property key", required = true) Strin public Table roleList() { File directory = this.roleService.getRolesVarPath(); List rolesNames = this.roleService.getRoleNames(directory); - Stream header = Stream.of(new String[] { "Name" }); - Stream rows; - if (rolesNames != null) { - rows = rolesNames.stream().map(tr -> new String[] { tr }); - } - else { - rows = Stream.empty(); - } - List allRows = rows.collect(Collectors.toList()); - String[][] data = Stream.concat(header, allRows.stream()).toArray(String[][]::new); - TableModel model = new ArrayTableModel(data); - TableBuilder tableBuilder = new TableBuilder(model); - return tableBuilder.addFullBorder(BorderStyle.fancy_light).build(); + return TableUtils.buildTable(rolesNames, "Name"); } public RoleService getRoleService() { diff --git a/src/main/java/org/springframework/cli/merger/ProjectMerger.java b/src/main/java/org/springframework/cli/merger/ProjectMerger.java index 6eea322d..2f045bc2 100644 --- a/src/main/java/org/springframework/cli/merger/ProjectMerger.java +++ b/src/main/java/org/springframework/cli/merger/ProjectMerger.java @@ -172,26 +172,20 @@ public void merge() { } private void mergeSpringBootApplicationClassAnnotations() throws IOException { - logger.debug("Looking for @SpringBootApplication in directory " + this.toMergeProjectPath.toFile()); Optional springBootApplicationFile = RootPackageFinder .findSpringBootApplicationFile(this.toMergeProjectPath.toFile()); if (springBootApplicationFile.isPresent()) { - CollectAnnotationAndImportInformationRecipe collectAnnotationAndImportInformationRecipe = new CollectAnnotationAndImportInformationRecipe(); - Consumer onError = e -> { - logger.error("error in javaParser execution", e); - }; + Consumer onError = e -> logger.error("error in javaParser execution", e); + List mergeCompilationUnits = parseJavaFile(springBootApplicationFile.get().toPath(), onError); + + CollectAnnotationAndImportInformationRecipe annotationImportRecipe = new CollectAnnotationAndImportInformationRecipe(); InMemoryExecutionContext executionContext = new InMemoryExecutionContext(onError); - List paths = new ArrayList<>(); - paths.add(springBootApplicationFile.get().toPath()); - JavaParser javaParser = new Java17Parser.Builder().build(); - List compilationUnits = javaParser.parse(paths, null, executionContext).toList(); - collectAnnotationAndImportInformationRecipe.run(new InMemoryLargeSourceSet(compilationUnits), - executionContext); + runRecipeOnFile(annotationImportRecipe, mergeCompilationUnits, executionContext); - List declaredAnnotations = collectAnnotationAndImportInformationRecipe.getDeclaredAnnotations(); - List declaredImports = collectAnnotationAndImportInformationRecipe.getDeclaredImports(); + List declaredAnnotations = annotationImportRecipe.getDeclaredAnnotations(); + List declaredImports = annotationImportRecipe.getDeclaredImports(); Map annotationImportMap = new HashMap<>(); for (Annotation declaredAnnotation : declaredAnnotations) { @@ -199,7 +193,6 @@ private void mergeSpringBootApplicationClassAnnotations() throws IOException { continue; } for (String declaredImport : declaredImports) { - // get the import statement that matches the annotation if (declaredImport.contains(declaredAnnotation.getSimpleName())) { annotationImportMap.put(declaredAnnotation.toString(), declaredImport); } @@ -210,20 +203,18 @@ private void mergeSpringBootApplicationClassAnnotations() throws IOException { Optional currentSpringBootApplicationFile = RootPackageFinder .findSpringBootApplicationFile(this.currentProjectPath.toFile()); if (currentSpringBootApplicationFile.isPresent()) { + List currentCompilationUnits = parseJavaFile( + currentSpringBootApplicationFile.get().toPath(), onError); executionContext = new InMemoryExecutionContext(onError); - paths = new ArrayList<>(); - paths.add(currentSpringBootApplicationFile.get().toPath()); - javaParser = new Java17Parser.Builder().build(); - compilationUnits = javaParser.parse(paths, null, executionContext).toList(); + for (Entry annotationImportEntry : annotationImportMap.entrySet()) { String annotation = annotationImportEntry.getKey(); String importStatement = annotationImportEntry.getValue(); + AddImport addImport = new AddImport(importStatement, null, false); AddImportRecipe addImportRecipe = new AddImportRecipe(addImport); - List results = addImportRecipe - .run(new InMemoryLargeSourceSet(compilationUnits), executionContext) - .getChangeset() - .getAllResults(); + List results = runRecipeOnFile(addImportRecipe, currentCompilationUnits, executionContext); + updateSpringApplicationClass(currentSpringBootApplicationFile.get().toPath(), results); AttributedStringBuilder sb = new AttributedStringBuilder(); @@ -232,15 +223,24 @@ private void mergeSpringBootApplicationClassAnnotations() throws IOException { terminalMessage.print(sb.toAttributedString()); injectAnnotation(currentSpringBootApplicationFile.get().toPath(), annotation); - // AddAnnotationToClassRecipe addAnnotationToClassRecipe = new - // AddAnnotationToClassRecipe(annotation); - // results = addAnnotationToClassRecipe.run(compilationUnits); - // updateSpringApplicationClass(currentSpringBootApplicationFile.get().toPath(), - // results); } } } + } + + private List parseJavaFile(Path filePath, Consumer onError) { + InMemoryExecutionContext executionContext = new InMemoryExecutionContext(onError); + List paths = new ArrayList<>(); + paths.add(filePath); + JavaParser javaParser = new Java17Parser.Builder().build(); + return javaParser.parse(paths, null, executionContext).toList(); + } + private List runRecipeOnFile(Recipe recipe, List compilationUnits, + InMemoryExecutionContext executionContext) { + return recipe.run(new InMemoryLargeSourceSet(compilationUnits), executionContext) + .getChangeset() + .getAllResults(); } private void injectAnnotation(Path pathToFile, String annotation) { diff --git a/src/main/java/org/springframework/cli/merger/ai/OpenAiHandler.java b/src/main/java/org/springframework/cli/merger/ai/OpenAiHandler.java index 6f7fa970..abc2cdd5 100644 --- a/src/main/java/org/springframework/cli/merger/ai/OpenAiHandler.java +++ b/src/main/java/org/springframework/cli/merger/ai/OpenAiHandler.java @@ -56,13 +56,13 @@ public void add(String description, String path, boolean preview, boolean rewrit TerminalMessage terminalMessage) { Path projectPath = getProjectPath(path); - ProjectNameHeuristicAiService projectNameHeuristic = new ProjectNameHeuristicAiService(terminalMessage); + ProjectNameHeuristicAiService projectNameHeuristic = new ProjectNameHeuristicAiService(); logger.debug("Deriving main Spring project required..."); ProjectName projectName = projectNameHeuristic.deriveProjectName(description); logger.debug("Done. The code will primarily use " + projectName.getSpringProjectName()); if (rewrite) { - DescriptionRewriteAiService descriptionRewriteAiService = new DescriptionRewriteAiService(terminalMessage); + DescriptionRewriteAiService descriptionRewriteAiService = new DescriptionRewriteAiService(); description = descriptionRewriteAiService.rewrite(description); } terminalMessage.print(""); diff --git a/src/main/java/org/springframework/cli/merger/ai/ProjectArtifactProcessor.java b/src/main/java/org/springframework/cli/merger/ai/ProjectArtifactProcessor.java index 5ce9b8a5..69a33ba2 100644 --- a/src/main/java/org/springframework/cli/merger/ai/ProjectArtifactProcessor.java +++ b/src/main/java/org/springframework/cli/merger/ai/ProjectArtifactProcessor.java @@ -80,10 +80,10 @@ private ProcessArtifactResult processArtifacts(List proje ProjectArtifactType artifactType = projectArtifact.getArtifactType(); switch (artifactType) { case SOURCE_CODE: - writeSourceCode(projectArtifact, projectPath); + writeCode(projectArtifact, projectPath, false); break; case TEST_CODE: - writeTestCode(projectArtifact, projectPath); + writeCode(projectArtifact, projectPath, true); break; case MAVEN_DEPENDENCIES: writeMavenDependencies(projectArtifact, projectPath, terminalMessage); @@ -110,29 +110,24 @@ private ProcessArtifactResult processArtifacts(List proje return processArtifactResult; } - private void writeSourceCode(ProjectArtifact projectArtifact, Path projectPath) throws IOException { + private void writeCode(ProjectArtifact projectArtifact, Path projectPath, boolean isTest) throws IOException { String packageName = this.calculatePackageForArtifact(projectArtifact); ClassNameExtractor classNameExtractor = new ClassNameExtractor(); Optional className = classNameExtractor.extractClassName(projectArtifact.getText()); if (className.isPresent()) { - Path output = createSourceFile(projectPath, packageName, className.get() + ".java"); + Path output = createCodeFile(projectPath, packageName, className.get() + ".java", isTest); try (Writer writer = new BufferedWriter(new FileWriter(output.toFile()))) { writer.write(projectArtifact.getText()); } } } - private void writeTestCode(ProjectArtifact projectArtifact, Path projectPath) throws IOException { - // TODO parameterize better to reduce code duplication - String packageName = this.calculatePackageForArtifact(projectArtifact); - ClassNameExtractor classNameExtractor = new ClassNameExtractor(); - Optional className = classNameExtractor.extractClassName(projectArtifact.getText()); - if (className.isPresent()) { - Path output = createTestFile(projectPath, packageName, className.get() + ".java"); - try (Writer writer = new BufferedWriter(new FileWriter(output.toFile()))) { - writer.write(projectArtifact.getText()); - } - } + private Path createCodeFile(Path projectPath, String packageName, String fileName, boolean isTest) + throws IOException { + Path sourceFile = isTest ? resolveTestFile(projectPath, packageName, fileName) + : resolveSourceFile(projectPath, packageName, fileName); + createFile(sourceFile); + return sourceFile; } private void writeMavenDependencies(ProjectArtifact projectArtifact, Path projectPath, @@ -276,18 +271,6 @@ private static String extractFilenameFromComment(String content) { return null; } - private Path createSourceFile(Path projectPath, String packageName, String fileName) throws IOException { - Path sourceFile = resolveSourceFile(projectPath, packageName, fileName); - createFile(sourceFile); - return sourceFile; - } - - private Path createTestFile(Path projectPath, String packageName, String fileName) throws IOException { - Path sourceFile = resolveTestFile(projectPath, packageName, fileName); - createFile(sourceFile); - return sourceFile; - } - public Path resolveSourceFile(Path projectPath, String packageName, String fileName) { Path sourceDirectory = projectPath.resolve("src").resolve("main").resolve("java"); return resolvePackage(sourceDirectory, packageName).resolve(fileName); diff --git a/src/main/java/org/springframework/cli/merger/ai/service/AbstractOpenAiService.java b/src/main/java/org/springframework/cli/merger/ai/service/AbstractOpenAiService.java index a5249164..51cb0ff2 100644 --- a/src/main/java/org/springframework/cli/merger/ai/service/AbstractOpenAiService.java +++ b/src/main/java/org/springframework/cli/merger/ai/service/AbstractOpenAiService.java @@ -35,7 +35,6 @@ import org.springframework.cli.merger.ai.PromptRequest; import org.springframework.cli.runtime.engine.templating.HandlebarsTemplateEngine; import org.springframework.cli.util.PropertyFileUtils; -import org.springframework.cli.util.TerminalMessage; import org.springframework.core.io.ClassPathResource; import org.springframework.util.StreamUtils; @@ -45,12 +44,6 @@ public abstract class AbstractOpenAiService implements org.springframework.cli.m private com.theokanning.openai.service.OpenAiService openAiService; - private final TerminalMessage terminalMessage; - - public AbstractOpenAiService(TerminalMessage terminalMessage) { - this.terminalMessage = terminalMessage; - } - protected ChatCompletionRequest getChatCompletionRequest(PromptRequest promptRequest) { createOpenAiService(); ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() @@ -79,10 +72,6 @@ public HandlebarsTemplateEngine getHandlebarsTemplateEngine() { return handlebarsTemplateEngine; } - public TerminalMessage getTerminalMessage() { - return terminalMessage; - } - protected Map getContext(String description) { Map context = new HashMap<>(); context.put("description", description); @@ -97,12 +86,11 @@ protected String getPrompt(Map context, String promptType) { return getHandlebarsTemplateEngine().process(promptRaw, context); } catch (FileNotFoundException ex) { - throw new SpringCliException("Resource file note found:" + resourceFileName); + throw new SpringCliException("Resource file not found:" + resourceFileName); } catch (IOException ex) { - throw new SpringCliException("Could read file " + resourceFileName, ex); + throw new SpringCliException("Could not read file " + resourceFileName, ex); } - } protected PromptRequest createPromptRequest(Map context, String promptFamilyName) { diff --git a/src/main/java/org/springframework/cli/merger/ai/service/DescriptionRewriteAiService.java b/src/main/java/org/springframework/cli/merger/ai/service/DescriptionRewriteAiService.java index f9d2a329..ec01b062 100644 --- a/src/main/java/org/springframework/cli/merger/ai/service/DescriptionRewriteAiService.java +++ b/src/main/java/org/springframework/cli/merger/ai/service/DescriptionRewriteAiService.java @@ -21,14 +21,9 @@ import com.theokanning.openai.completion.chat.ChatCompletionRequest; import org.springframework.cli.merger.ai.PromptRequest; -import org.springframework.cli.util.TerminalMessage; public class DescriptionRewriteAiService extends AbstractOpenAiService { - public DescriptionRewriteAiService(TerminalMessage terminalMessage) { - super(terminalMessage); - } - public String rewrite(String description) { Map context = getContext(description); return generate(context); diff --git a/src/main/java/org/springframework/cli/merger/ai/service/GenerateCodeAiService.java b/src/main/java/org/springframework/cli/merger/ai/service/GenerateCodeAiService.java index 5aa0a476..9632d727 100644 --- a/src/main/java/org/springframework/cli/merger/ai/service/GenerateCodeAiService.java +++ b/src/main/java/org/springframework/cli/merger/ai/service/GenerateCodeAiService.java @@ -30,14 +30,16 @@ public class GenerateCodeAiService extends AbstractOpenAiService { + private final TerminalMessage terminalMessage; + public GenerateCodeAiService(TerminalMessage terminalMessage) { - super(terminalMessage); + this.terminalMessage = terminalMessage; } public String generate(Map context) { PromptRequest promptRequest = createPromptRequest(context, "ai-add"); String completionEstimate = getCompletionEstimate(); - getTerminalMessage() + terminalMessage .print("Generating code. This will take a few minutes ..." + " Check back around " + completionEstimate); ChatCompletionRequest chatCompletionRequest = getChatCompletionRequest(promptRequest); String response = getResponse(chatCompletionRequest); diff --git a/src/main/java/org/springframework/cli/merger/ai/service/ProjectNameHeuristicAiService.java b/src/main/java/org/springframework/cli/merger/ai/service/ProjectNameHeuristicAiService.java index 169ff89d..66ce96af 100644 --- a/src/main/java/org/springframework/cli/merger/ai/service/ProjectNameHeuristicAiService.java +++ b/src/main/java/org/springframework/cli/merger/ai/service/ProjectNameHeuristicAiService.java @@ -26,15 +26,10 @@ import org.springframework.cli.SpringCliException; import org.springframework.cli.merger.ai.ProjectName; import org.springframework.cli.merger.ai.PromptRequest; -import org.springframework.cli.util.TerminalMessage; import org.springframework.util.StringUtils; public class ProjectNameHeuristicAiService extends AbstractOpenAiService { - public ProjectNameHeuristicAiService(TerminalMessage terminalMessage) { - super(terminalMessage); - } - public ProjectName deriveProjectName(String description) { Map context = getContext(description); String response = generate(context); @@ -67,7 +62,6 @@ private ProjectName createProjectName(String response) { shortName = "ai"; } return new ProjectName(shortName.toLowerCase(), springProjectName); - } } diff --git a/src/main/java/org/springframework/cli/runtime/command/CommandScanner.java b/src/main/java/org/springframework/cli/runtime/command/CommandScanner.java index 621e92cf..0dcc0075 100644 --- a/src/main/java/org/springframework/cli/runtime/command/CommandScanner.java +++ b/src/main/java/org/springframework/cli/runtime/command/CommandScanner.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -30,6 +31,7 @@ import org.slf4j.LoggerFactory; import org.springframework.cli.SpringCliException; +import org.springframework.cli.util.IoUtils; /** * Scans a directory, by default .spring/commands in the current working directory, and @@ -42,7 +44,7 @@ */ public class CommandScanner { - private final Logger logger = LoggerFactory.getLogger(CommandScanner.class); + private static final Logger logger = LoggerFactory.getLogger(CommandScanner.class); private Path pathToScan; @@ -122,4 +124,12 @@ private Command getCommandObject(File directory) { return command; } + public static CommandScanResults scanCommands() { + Path cwd = IoUtils.getWorkingDirectory().toAbsolutePath(); + Path pathToUse = Paths.get(cwd.toString(), ".spring", "commands"); + logger.debug("Looking for user-defined commands in directory " + pathToUse); + CommandScanner scanner = new CommandScanner(pathToUse); + return scanner.scan(); + } + } diff --git a/src/main/java/org/springframework/cli/runtime/command/DynamicCommand.java b/src/main/java/org/springframework/cli/runtime/command/DynamicCommand.java index 6572f278..9fa32cc3 100644 --- a/src/main/java/org/springframework/cli/runtime/command/DynamicCommand.java +++ b/src/main/java/org/springframework/cli/runtime/command/DynamicCommand.java @@ -172,21 +172,22 @@ private void addRoleVariables(Map model, CommandContext commandC } private boolean usedDefaultValue(String variableName, CommandContext commandContext) { - boolean usedDefaultValue = false; - // Look for matching option of the provided variable name and determine if a - // default value was used. List commandParserResults = commandContext.getParserResults().results(); for (CommandParserResult commandParserResult : commandParserResults) { String optionName = NamingUtils.toKebab(commandParserResult.option().getLongNames()[0]); - if (variableName.equals(optionName)) { + if (variableName.equals(optionName)) { // Inline isName since it's a simple equals check Object defaultOptionValue = commandParserResult.option().getDefaultValue(); Object optionValue = commandParserResult.value(); - if (defaultOptionValue != null && optionValue != null && defaultOptionValue.equals(optionValue)) { - usedDefaultValue = true; + if (isDefaultValueMatching(defaultOptionValue, optionValue)) { + return true; } } } - return usedDefaultValue; + return false; + } + + private boolean isDefaultValueMatching(Object defaultOptionValue, Object optionValue) { + return defaultOptionValue != null && optionValue != null && defaultOptionValue.equals(optionValue); } public void runCommand(Path workingDirectory, String springDir, String commandsDir, Map model) { diff --git a/src/main/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolver.java b/src/main/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolver.java index 57a6f4f9..a4a9b890 100644 --- a/src/main/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolver.java +++ b/src/main/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolver.java @@ -16,8 +16,6 @@ package org.springframework.cli.runtime.command; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -31,7 +29,6 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.cli.runtime.engine.model.ModelPopulator; -import org.springframework.cli.util.IoUtils; import org.springframework.cli.util.TerminalMessage; import org.springframework.shell.command.CommandRegistration; import org.springframework.shell.command.CommandRegistration.BuilderSupplier; @@ -71,19 +68,19 @@ public DynamicMethodCommandResolver(Collection modelPopulators, @Override public List resolve() { - CommandScanResults commandScanResults = scanCommands(); + CommandScanResults commandScanResults = CommandScanner.scanCommands(); log.debug("Found commands " + commandScanResults); return registerSpringCliCommands(commandScanResults, modelPopulators, builder); } - protected CommandScanResults scanCommands() { - Path cwd = IoUtils.getWorkingDirectory().toAbsolutePath(); - Path pathToUse = Paths.get(cwd.toString(), ".spring", "commands"); - log.debug("Looking for user-defined commands in directory " + pathToUse); - CommandScanner scanner = new CommandScanner(pathToUse); - CommandScanResults commandScanResults = scanner.scan(); - return commandScanResults; - } + // protected CommandScanResults scanCommands() { + // Path cwd = IoUtils.getWorkingDirectory().toAbsolutePath(); + // Path pathToUse = Paths.get(cwd.toString(), ".spring", "commands"); + // log.debug("Looking for user-defined commands in directory " + pathToUse); + // CommandScanner scanner = new CommandScanner(pathToUse); + // CommandScanResults commandScanResults = scanner.scan(); + // return commandScanResults; + // } private List registerSpringCliCommands(CommandScanResults results, Collection modelPopulators, CommandRegistration.BuilderSupplier builderSupplier) { diff --git a/src/main/java/org/springframework/cli/util/TableUtils.java b/src/main/java/org/springframework/cli/util/TableUtils.java new file mode 100644 index 00000000..17024693 --- /dev/null +++ b/src/main/java/org/springframework/cli/util/TableUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021 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 + * + * https://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.cli.util; + +import java.util.List; +import java.util.stream.Stream; + +import org.springframework.shell.table.ArrayTableModel; +import org.springframework.shell.table.BorderStyle; +import org.springframework.shell.table.Table; +import org.springframework.shell.table.TableBuilder; +import org.springframework.shell.table.TableModel; + +/** + * Utility class for building tables to display data in the CLI. + */ +public final class TableUtils { + + // Private constructor to prevent instantiation + private TableUtils() { + throw new AssertionError("Utility class - do not instantiate"); + } + + /** + * Builds a table with a single-column layout for displaying a list of items. + * @param data the list of items to display in the table + * @param header the header for the table column + * @return a formatted Table object + */ + public static Table buildTable(List data, String header) { + Stream headerStream = Stream.of(new String[] { header }); + Stream rows = (data != null) ? data.stream().map(item -> new String[] { item }) : Stream.empty(); + String[][] tableData = Stream.concat(headerStream, rows).toArray(String[][]::new); + TableModel model = new ArrayTableModel(tableData); + TableBuilder tableBuilder = new TableBuilder(model); + return tableBuilder.addFullBorder(BorderStyle.fancy_light).build(); + } + +} diff --git a/src/test/java/org/springframework/cli/merger/ai/ProjectNameHeuristicAiServiceTests.java b/src/test/java/org/springframework/cli/merger/ai/ProjectNameHeuristicAiServiceTests.java index 32c260ff..87d6e5ce 100644 --- a/src/test/java/org/springframework/cli/merger/ai/ProjectNameHeuristicAiServiceTests.java +++ b/src/test/java/org/springframework/cli/merger/ai/ProjectNameHeuristicAiServiceTests.java @@ -21,7 +21,6 @@ import org.springframework.cli.CliTags; import org.springframework.cli.merger.ai.service.ProjectNameHeuristicAiService; -import org.springframework.cli.util.TerminalMessage; import static org.assertj.core.api.Assertions.assertThat; @@ -30,7 +29,7 @@ class ProjectNameHeuristicAiServiceTests { @Test @Tag(CliTags.AI) void deriveProjectName() { - ProjectNameHeuristicAiService projectNameHeuristic = new ProjectNameHeuristicAiService(TerminalMessage.noop()); + ProjectNameHeuristicAiService projectNameHeuristic = new ProjectNameHeuristicAiService(); ProjectName projectName = projectNameHeuristic.deriveProjectName("jpa"); diff --git a/src/test/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolverTests.java b/src/test/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolverTests.java index 0065f3e0..a4271e02 100644 --- a/src/test/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolverTests.java +++ b/src/test/java/org/springframework/cli/runtime/command/DynamicMethodCommandResolverTests.java @@ -23,7 +23,9 @@ import java.util.Map; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.cli.runtime.engine.model.ModelPopulator; import org.springframework.cli.runtime.engine.model.SystemModelPopulator; @@ -31,7 +33,9 @@ import org.springframework.shell.command.CommandRegistration; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; +@ExtendWith(MockitoExtension.class) class DynamicMethodCommandResolverTests { @Test @@ -71,30 +75,32 @@ void testResolving() { modelPopulators.add(systemModelPopulator); DynamicMethodCommandResolver resolver = new DynamicMethodCommandResolver(modelPopulators, () -> CommandRegistration.builder(), TerminalMessage.noop(), null); - // Move to mock scan so that we control results - DynamicMethodCommandResolver spy = Mockito.spy(resolver); - Mockito.when(spy.scanCommands()).thenReturn(commandScanResults); - List resolved = spy.resolve(); - - assertThat(resolved).hasSize(2); - assertThat(resolved).satisfiesExactly(registration -> { - assertThat(registration.getCommand()).isEqualTo("k8s-simple new"); - assertThat(registration.getDescription()).isEqualTo("subcommand description"); - assertThat(registration.getOptions()).hasSize(2); - assertThat(registration.getOptions().get(0)).satisfies(option -> { - assertThat(option.getLongNames()).contains("with-gusto"); - assertThat(option.getType().getType()).isEqualTo(Boolean.class); - assertThat(option.getDescription()).isEqualTo("what a nice simple option"); - assertThat(option.getDefaultValue()).isEqualTo("true"); - assertThat(option.isRequired()).isTrue(); - }); - assertThat(registration.getOptions().get(1)).satisfies(option -> { - assertThat(option.getLongNames()).contains("with-greeting"); - assertThat(option.getType().getType()).isEqualTo(String.class); + + try (MockedStatic mockedCommandScanner = mockStatic(CommandScanner.class)) { + mockedCommandScanner.when(CommandScanner::scanCommands).thenReturn(commandScanResults); + + List resolved = resolver.resolve(); + + assertThat(resolved).hasSize(2); + assertThat(resolved).satisfiesExactly(registration -> { + assertThat(registration.getCommand()).isEqualTo("k8s-simple new"); + assertThat(registration.getDescription()).isEqualTo("subcommand description"); + assertThat(registration.getOptions()).hasSize(2); + assertThat(registration.getOptions().get(0)).satisfies(option -> { + assertThat(option.getLongNames()).contains("with-gusto"); + assertThat(option.getType().getType()).isEqualTo(Boolean.class); + assertThat(option.getDescription()).isEqualTo("what a nice simple option"); + assertThat(option.getDefaultValue()).isEqualTo("true"); + assertThat(option.isRequired()).isTrue(); + }); + assertThat(registration.getOptions().get(1)).satisfies(option -> { + assertThat(option.getLongNames()).contains("with-greeting"); + assertThat(option.getType().getType()).isEqualTo(String.class); + }); + }, registration -> { + assertThat(registration.getCommand()).isEqualTo("k8s-simple new-services"); }); - }, registration -> { - assertThat(registration.getCommand()).isEqualTo("k8s-simple new-services"); - }); + } } }