diff --git a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusConfigCommandResult.java b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusConfigCommandResult.java index 879335b56..6b121b39a 100644 --- a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusConfigCommandResult.java +++ b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusConfigCommandResult.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.smallrye.common.os.OS; + public class QuarkusConfigCommandResult { final String applicationPropertiesAsString; @@ -19,10 +21,24 @@ public QuarkusConfigCommandResult assertCommandOutputNotContains(String expected } public QuarkusConfigCommandResult assertCommandOutputContains(String expected) { - assertTrue(output.contains(expected.trim()), "Expected output '" + output + "' does not contain '" + expected + "'"); + if (OS.WINDOWS.isCurrent()) { + String windowsEscapedExpected = normalizeString(expected); + String windowsEscapedOutput = normalizeString(output); + + assertTrue(windowsEscapedOutput.contains(windowsEscapedExpected), + "Expected output '" + windowsEscapedExpected + "'does not contain '" + windowsEscapedOutput + "'"); + } else { + assertTrue(output.contains(expected.trim()), + "Expected output '" + output + "' does not contain '" + expected + "'"); + } return this; } + private String normalizeString(String str) { + String noAnsi = str.replaceAll("\\x1B\\[[;\\d]*m", ""); + return noAnsi.replaceAll("\"", "").replaceAll("\n", " ").trim(); + } + public QuarkusConfigCommandResult assertApplicationPropertiesContains(String str) { assertTrue(applicationPropertiesAsString.contains(str), "Expected value '" + str + "' is missing in application.properties: " + applicationPropertiesAsString); diff --git a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandBuilder.java b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandBuilder.java index 35bbda04b..8a6fe793c 100644 --- a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandBuilder.java +++ b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandBuilder.java @@ -10,8 +10,10 @@ import org.junit.jupiter.api.Assertions; import io.quarkus.test.logging.Log; +import io.quarkus.test.util.QuarkusCLIUtils; import io.quarkus.test.utils.Command; import io.quarkus.test.utils.FileUtils; +import io.smallrye.common.os.OS; public class QuarkusEncryptConfigCommandBuilder { @@ -31,7 +33,12 @@ public class QuarkusEncryptConfigCommandBuilder { } public QuarkusEncryptConfigCommandBuilder secret(String secret) { - this.secret = secret; + if (OS.WINDOWS.isCurrent()) { + this.secret = QuarkusCLIUtils.escapeSecretCharsForWindows(secret); + } else { + this.secret = secret; + } + return this; } diff --git a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandResult.java b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandResult.java index 01ddf5056..93cbe7ae6 100644 --- a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandResult.java +++ b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusEncryptConfigCommandResult.java @@ -5,6 +5,8 @@ import java.util.Objects; import java.util.function.Consumer; +import io.quarkus.test.util.QuarkusCLIUtils; + public class QuarkusEncryptConfigCommandResult extends QuarkusConfigCommandResult { private static final String SECRET_ENCRYPTED_TO = "was encrypted to"; @@ -19,7 +21,11 @@ public class QuarkusEncryptConfigCommandResult extends QuarkusConfigCommandResul public String getGeneratedEncryptionKey() { if (output.contains(WITH_GENERATED_KEY)) { - return output.transform(o -> o.substring(o.lastIndexOf(" "))).trim(); + return output + .transform(o -> o.substring(o.lastIndexOf(" "))) + .transform(QuarkusCLIUtils::toUtf8) + .transform(QuarkusCLIUtils::removeAnsiAndHiddenChars) + .trim(); } return null; } @@ -29,6 +35,8 @@ public String getEncryptedSecret() { encryptedSecret = output .transform(o -> o.split(SECRET_ENCRYPTED_TO)[1]) .transform(remaining -> remaining.split(WITH_GENERATED_KEY)[0]) + .transform(QuarkusCLIUtils::toUtf8) + .transform(QuarkusCLIUtils::removeAnsiAndHiddenChars) .trim(); } return encryptedSecret; diff --git a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusSetConfigCommandBuilder.java b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusSetConfigCommandBuilder.java index 31cc62a9c..5ef2f66a1 100644 --- a/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusSetConfigCommandBuilder.java +++ b/quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/config/QuarkusSetConfigCommandBuilder.java @@ -2,6 +2,9 @@ import java.util.ArrayList; +import io.quarkus.test.util.QuarkusCLIUtils; +import io.smallrye.common.os.OS; + public class QuarkusSetConfigCommandBuilder { private final boolean updateScenario; @@ -23,7 +26,11 @@ public QuarkusSetConfigCommandBuilder name(String name) { } public QuarkusSetConfigCommandBuilder value(String value) { - this.propertyValue = value; + if (OS.WINDOWS.isCurrent()) { + this.propertyValue = QuarkusCLIUtils.escapeSecretCharsForWindows(value); + } else { + this.propertyValue = value; + } return this; } diff --git a/quarkus-test-cli/src/main/java/io/quarkus/test/util/QuarkusCLIUtils.java b/quarkus-test-cli/src/main/java/io/quarkus/test/util/QuarkusCLIUtils.java index 92bf743c2..d847e7995 100644 --- a/quarkus-test-cli/src/main/java/io/quarkus/test/util/QuarkusCLIUtils.java +++ b/quarkus-test-cli/src/main/java/io/quarkus/test/util/QuarkusCLIUtils.java @@ -11,6 +11,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; @@ -27,12 +29,15 @@ import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import io.quarkus.test.bootstrap.QuarkusCliRestService; +import io.smallrye.common.os.OS; public abstract class QuarkusCLIUtils { public static final String RESOURCES_DIR = Paths.get("src", "main", "resources").toString(); public static final String PROPERTIES_FILE = "application.properties"; public static final String PROPERTIES_YAML_FILE = "application.yml"; public static final String POM_FILE = "pom.xml"; + private static final String ANSI_BOLD_TEXT_ESCAPE_SEQ = "[1m"; + private static final char ESCAPE_CHARACTER = 27; /** * This constant stands for number of fields in groupId:artifactId:version string, when separated via ":". @@ -375,4 +380,45 @@ public String toString() { return "Plugin {groupId=" + getGroupId() + ", artifactId=" + getArtifactId() + ", version=" + getVersion() + "}"; } } + + /** + * Escapes a command-line secret chars for Windows OS. + */ + public static String escapeSecretCharsForWindows(String secret) { + return "\"" + secret + .replace("\"", "\\\"") + + "\""; + } + + /** + * When Quarkus CLI prints out text, especially by {@code quarkus config encrypt} command, + * important parts (like encoded secrets) can be highlighted or there can be hidden chars. + * We recognize hidden chars etc. This method handles both situation. It's definitely imperfect, + * but we only deal with scenarios (issues) we run on. + */ + public static String removeAnsiAndHiddenChars(String text) { + if (OS.current() == OS.WINDOWS) { + var result = text + .trim() + .transform(t -> { + if (t.contains(ANSI_BOLD_TEXT_ESCAPE_SEQ)) { + return t.substring(ANSI_BOLD_TEXT_ESCAPE_SEQ.length()); + } + return t; + }) + .transform(t -> { + int idx = t.indexOf(ESCAPE_CHARACTER); + if (idx >= 0) { + return t.substring(0, idx); + } + return t; + }); + return result; + } + return text; + } + + public static String toUtf8(String t) { + return new String(t.getBytes(Charset.defaultCharset()), StandardCharsets.UTF_8); + } }