feat(qqq): add prompt validation with retry loop (#24)
- Add PromptValidation record for validation rules (pattern, length, range, enum) - Add PromptValidator with support for required, pattern, length, range, and enum validation - Update PromptRunner to validate and re-prompt on failure - Validate --var CLI overrides, fallback to interactive prompt if invalid - Add ConsoleUI methods for validation feedback (error, success, warning) - Add 20 unit tests for PromptValidator Closes #24
This commit is contained in:
6 files changed
+788
-8
No files matched your search
@@ -34,6 +34,8 @@ public class ConsoleUI
|
||||
private static final String ANSI_RESET = "\u001B[0m";
|
||||
private static final String ANSI_DIM = "\u001B[2m";
|
||||
private static final String ANSI_GREEN = "\u001B[32m";
|
||||
private static final String ANSI_RED = "\u001B[31m";
|
||||
private static final String ANSI_YELLOW = "\u001B[33m";
|
||||
|
||||
private final Console console;
|
||||
private final Scanner scanner;
|
||||
@@ -132,6 +134,44 @@ public class ConsoleUI
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Print a validation error message inline.
|
||||
*
|
||||
* @param text the error message
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
public void validationError(String text)
|
||||
{
|
||||
System.out.println(" " + ANSI_RED + "✗ " + text + ANSI_RESET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Print a validation success indicator.
|
||||
*
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
public void validationSuccess()
|
||||
{
|
||||
System.out.println(" " + ANSI_GREEN + "✓" + ANSI_RESET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Print a warning message.
|
||||
*
|
||||
* @param text the warning message
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
public void warning(String text)
|
||||
{
|
||||
System.out.println(ANSI_YELLOW + "warning: " + text + ANSI_RESET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Print a blank line.
|
||||
*
|
||||
|
||||
@@ -23,12 +23,14 @@ import java.util.Map;
|
||||
* Runs interactive prompts to collect template variables.
|
||||
*
|
||||
* Uses ConsoleUI for all interactive input. Supports text input and selections.
|
||||
* Validates input against prompt rules and re-prompts on failure.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*******************************************************************************/
|
||||
public class PromptRunner
|
||||
{
|
||||
private final ConsoleUI ui;
|
||||
private final PromptValidator validator;
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +42,7 @@ public class PromptRunner
|
||||
public PromptRunner()
|
||||
{
|
||||
this.ui = new ConsoleUI();
|
||||
this.validator = new PromptValidator();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +56,7 @@ public class PromptRunner
|
||||
public PromptRunner(ConsoleUI ui)
|
||||
{
|
||||
this.ui = ui;
|
||||
this.validator = new PromptValidator();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +81,26 @@ public class PromptRunner
|
||||
|
||||
for(TemplateManifest.Prompt prompt : prompts)
|
||||
{
|
||||
// Skip if already provided via CLI
|
||||
// Check if provided via CLI
|
||||
if(overrides.containsKey(prompt.name()))
|
||||
{
|
||||
String value = overrides.get(prompt.name());
|
||||
ui.showValue(prompt.message() != null ? prompt.message() : prompt.name(), value);
|
||||
results.put(prompt.name(), value);
|
||||
String label = prompt.message() != null ? prompt.message() : prompt.name();
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, value);
|
||||
|
||||
if(result.valid())
|
||||
{
|
||||
ui.showValue(label, value);
|
||||
results.put(prompt.name(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.showValue(label, value);
|
||||
ui.validationError(result.errorMessage());
|
||||
ui.warning("Invalid --var value, prompting interactively");
|
||||
String corrected = promptForValue(prompt);
|
||||
results.put(prompt.name(), corrected);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -96,10 +114,12 @@ public class PromptRunner
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Prompt for a single value.
|
||||
* Prompt for a single value with validation.
|
||||
*
|
||||
* Re-prompts on validation failure until valid input is provided.
|
||||
*
|
||||
* @param prompt prompt definition
|
||||
* @return user input value
|
||||
* @return validated user input value
|
||||
* @since 0.1.0
|
||||
***************************************************************************/
|
||||
private String promptForValue(TemplateManifest.Prompt prompt)
|
||||
@@ -111,9 +131,24 @@ public class PromptRunner
|
||||
{
|
||||
return promptSelect(message, prompt.choices(), prompt.defaultValue());
|
||||
}
|
||||
else
|
||||
|
||||
boolean hasValidation = prompt.isRequired() || prompt.validation() != null;
|
||||
|
||||
while(true)
|
||||
{
|
||||
return ui.promptText(message, prompt.defaultValue());
|
||||
String value = ui.promptText(message, prompt.defaultValue());
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, value);
|
||||
|
||||
if(result.valid())
|
||||
{
|
||||
if(hasValidation)
|
||||
{
|
||||
ui.validationSuccess();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
ui.validationError(result.errorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Copyright (c) 2025. QRunIO. Contact: contact@qrun.io
|
||||
*
|
||||
* THE CONTENTS OF THIS PROJECT ARE PROPRIETARY AND CONFIDENTIAL.
|
||||
* UNAUTHORIZED COPYING, TRANSFERRING, OR REPRODUCTION OF ANY PART OF THIS PROJECT, VIA ANY MEDIUM, IS STRICTLY PROHIBITED.
|
||||
*
|
||||
* The receipt or possession of the source code and/or any parts thereof does not convey or imply any right to use them
|
||||
* for any purpose other than the purpose for which they were provided to you.
|
||||
*/
|
||||
|
||||
package io.qrun.qctl.qqq.template;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Validation rules for template prompts.
|
||||
*
|
||||
* Supports string validation (pattern, length) and integer validation (range).
|
||||
*
|
||||
* @param pattern regex pattern for string validation
|
||||
* @param minLength minimum string length
|
||||
* @param maxLength maximum string length
|
||||
* @param min minimum value for integers
|
||||
* @param max maximum value for integers
|
||||
* @param values allowed values (enum validation)
|
||||
* @param message custom error message
|
||||
* @since 0.2.0
|
||||
*******************************************************************************/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record PromptValidation(
|
||||
String pattern,
|
||||
Integer minLength,
|
||||
Integer maxLength,
|
||||
Integer min,
|
||||
Integer max,
|
||||
List<String> values,
|
||||
String message
|
||||
)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Copyright (c) 2025. QRunIO. Contact: contact@qrun.io
|
||||
*
|
||||
* THE CONTENTS OF THIS PROJECT ARE PROPRIETARY AND CONFIDENTIAL.
|
||||
* UNAUTHORIZED COPYING, TRANSFERRING, OR REPRODUCTION OF ANY PART OF THIS PROJECT, VIA ANY MEDIUM, IS STRICTLY PROHIBITED.
|
||||
*
|
||||
* The receipt or possession of the source code and/or any parts thereof does not convey or imply any right to use them
|
||||
* for any purpose other than the purpose for which they were provided to you.
|
||||
*/
|
||||
|
||||
package io.qrun.qctl.qqq.template;
|
||||
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Validates prompt values against validation rules.
|
||||
*
|
||||
* Supports pattern matching, length constraints, numeric ranges, and enum values.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*******************************************************************************/
|
||||
public class PromptValidator
|
||||
{
|
||||
/***************************************************************************
|
||||
* Validation result containing success status and error message.
|
||||
*
|
||||
* @param valid true if validation passed
|
||||
* @param errorMessage error message if validation failed
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
public record ValidationResult(boolean valid, String errorMessage)
|
||||
{
|
||||
/*************************************************************************
|
||||
* Create a successful validation result.
|
||||
*
|
||||
* @return success result
|
||||
* @since 0.2.0
|
||||
*************************************************************************/
|
||||
public static ValidationResult success()
|
||||
{
|
||||
return new ValidationResult(true, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Create a failed validation result.
|
||||
*
|
||||
* @param message the error message
|
||||
* @return failure result
|
||||
* @since 0.2.0
|
||||
*************************************************************************/
|
||||
public static ValidationResult failure(String message)
|
||||
{
|
||||
return new ValidationResult(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Validate a value against prompt rules.
|
||||
*
|
||||
* @param prompt the prompt definition with validation rules
|
||||
* @param value the value to validate
|
||||
* @return validation result
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
public ValidationResult validate(TemplateManifest.Prompt prompt, String value)
|
||||
{
|
||||
// Check required
|
||||
if(prompt.isRequired() && (value == null || value.isBlank()))
|
||||
{
|
||||
return ValidationResult.failure("This field is required");
|
||||
}
|
||||
|
||||
// If empty and not required, skip further validation
|
||||
if(value == null || value.isBlank())
|
||||
{
|
||||
return ValidationResult.success();
|
||||
}
|
||||
|
||||
// Get validation rules
|
||||
PromptValidation validation = prompt.validation();
|
||||
if(validation == null)
|
||||
{
|
||||
return ValidationResult.success();
|
||||
}
|
||||
|
||||
// Determine type
|
||||
String type = prompt.type() != null ? prompt.type().toLowerCase() : "text";
|
||||
|
||||
// Type-specific validation
|
||||
return switch(type)
|
||||
{
|
||||
case "integer", "number" -> validateInteger(validation, value);
|
||||
default -> validateString(validation, value);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Validate a string value.
|
||||
*
|
||||
* @param validation validation rules
|
||||
* @param value the value to validate
|
||||
* @return validation result
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
private ValidationResult validateString(PromptValidation validation, String value)
|
||||
{
|
||||
// Pattern validation
|
||||
if(validation.pattern() != null)
|
||||
{
|
||||
Optional<ValidationResult> patternResult = validatePattern(validation.pattern(), value, validation.message());
|
||||
if(patternResult.isPresent())
|
||||
{
|
||||
return patternResult.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Length validation
|
||||
if(validation.minLength() != null && value.length() < validation.minLength())
|
||||
{
|
||||
String message = validation.message() != null
|
||||
? validation.message()
|
||||
: "Must be at least " + validation.minLength() + " characters";
|
||||
return ValidationResult.failure(message);
|
||||
}
|
||||
|
||||
if(validation.maxLength() != null && value.length() > validation.maxLength())
|
||||
{
|
||||
String message = validation.message() != null
|
||||
? validation.message()
|
||||
: "Must be at most " + validation.maxLength() + " characters";
|
||||
return ValidationResult.failure(message);
|
||||
}
|
||||
|
||||
// Enum validation
|
||||
if(validation.values() != null && !validation.values().isEmpty())
|
||||
{
|
||||
if(!validation.values().contains(value))
|
||||
{
|
||||
String message = validation.message() != null
|
||||
? validation.message()
|
||||
: "Must be one of: " + String.join(", ", validation.values());
|
||||
return ValidationResult.failure(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.success();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Validate an integer value.
|
||||
*
|
||||
* @param validation validation rules
|
||||
* @param value the value to validate
|
||||
* @return validation result
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
private ValidationResult validateInteger(PromptValidation validation, String value)
|
||||
{
|
||||
// Parse as integer
|
||||
int intValue;
|
||||
try
|
||||
{
|
||||
intValue = Integer.parseInt(value.trim());
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
return ValidationResult.failure("Must be a valid integer");
|
||||
}
|
||||
|
||||
// Min validation
|
||||
if(validation.min() != null && intValue < validation.min())
|
||||
{
|
||||
String message = validation.message() != null
|
||||
? validation.message()
|
||||
: "Must be at least " + validation.min();
|
||||
return ValidationResult.failure(message);
|
||||
}
|
||||
|
||||
// Max validation
|
||||
if(validation.max() != null && intValue > validation.max())
|
||||
{
|
||||
String message = validation.message() != null
|
||||
? validation.message()
|
||||
: "Must be at most " + validation.max();
|
||||
return ValidationResult.failure(message);
|
||||
}
|
||||
|
||||
return ValidationResult.success();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Validate against a regex pattern.
|
||||
*
|
||||
* @param pattern the regex pattern
|
||||
* @param value the value to validate
|
||||
* @param customMessage custom error message
|
||||
* @return validation result if failed, empty if passed
|
||||
* @since 0.2.0
|
||||
***************************************************************************/
|
||||
private Optional<ValidationResult> validatePattern(String pattern, String value, String customMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
Pattern regex = Pattern.compile(pattern);
|
||||
if(!regex.matcher(value).matches())
|
||||
{
|
||||
String message = customMessage != null
|
||||
? customMessage
|
||||
: "Must match pattern: " + pattern;
|
||||
return Optional.of(ValidationResult.failure(message));
|
||||
}
|
||||
}
|
||||
catch(PatternSyntaxException e)
|
||||
{
|
||||
return Optional.of(ValidationResult.failure("Invalid validation pattern: " + pattern));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -96,10 +96,20 @@ public record TemplateManifest(
|
||||
String type,
|
||||
String defaultValue,
|
||||
List<String> choices,
|
||||
String validation,
|
||||
PromptValidation validation,
|
||||
Boolean required
|
||||
)
|
||||
{
|
||||
/*************************************************************************
|
||||
* Check if this prompt is required.
|
||||
*
|
||||
* @return true if required (defaults to true if not specified)
|
||||
* @since 0.2.0
|
||||
*************************************************************************/
|
||||
public boolean isRequired()
|
||||
{
|
||||
return required == null || required;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Copyright (c) 2025. QRunIO. Contact: contact@qrun.io
|
||||
*
|
||||
* THE CONTENTS OF THIS PROJECT ARE PROPRIETARY AND CONFIDENTIAL.
|
||||
* UNAUTHORIZED COPYING, TRANSFERRING, OR REPRODUCTION OF ANY PART OF THIS PROJECT, VIA ANY MEDIUM, IS STRICTLY PROHIBITED.
|
||||
*
|
||||
* The receipt or possession of the source code and/or any parts thereof does not convey or imply any right to use them
|
||||
* for any purpose other than the purpose for which they were provided to you.
|
||||
*/
|
||||
|
||||
package io.qrun.qctl.qqq.template;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Unit tests for PromptValidator.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*******************************************************************************/
|
||||
class PromptValidatorTest
|
||||
{
|
||||
private PromptValidator validator;
|
||||
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUp()
|
||||
{
|
||||
validator = new PromptValidator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test required field with blank value fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testRequiredFieldBlankFails()
|
||||
{
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, null, true);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).isEqualTo("This field is required");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test required field with null value fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testRequiredFieldNullFails()
|
||||
{
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, null, true);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, null);
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).isEqualTo("This field is required");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test required field with valid value passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testRequiredFieldWithValuePasses()
|
||||
{
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, null, true);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "John");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test optional field with blank value passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testOptionalFieldBlankPasses()
|
||||
{
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, null, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test pattern validation passes for valid input.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testPatternValidationPasses()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
"^[a-z][a-z0-9]*$", null, null, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "myapp123");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test pattern validation fails for invalid input.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testPatternValidationFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
"^[a-z][a-z0-9]*$", null, null, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "123invalid");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("pattern");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test custom error message is used on validation failure.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testCustomErrorMessage()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
"^[a-z]+$", null, null, null, null, null, "Must contain only lowercase letters");
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "ABC123");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).isEqualTo("Must contain only lowercase letters");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test minLength validation passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testMinLengthPasses()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, 3, null, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "abc");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test minLength validation fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testMinLengthFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, 5, null, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "ab");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("at least 5");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test maxLength validation passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testMaxLengthPasses()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, 10, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "short");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test maxLength validation fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testMaxLengthFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, 5, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "toolongvalue");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("at most 5");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test enum values validation passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testEnumValuesPasses()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, null, null, List.of("dev", "staging", "prod"), null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"env", "Environment", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "dev");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test enum values validation fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testEnumValuesFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, null, null, List.of("dev", "staging", "prod"), null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"env", "Environment", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "invalid");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("one of");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test integer validation passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testIntegerValidationPasses()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, 1, 100, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"count", "Count", "integer", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "50");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test integer min validation fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testIntegerMinFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, 10, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"count", "Count", "integer", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "5");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("at least 10");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test integer max validation fails.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testIntegerMaxFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, null, 100, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"count", "Count", "integer", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "200");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("at most 100");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test non-integer input fails for integer type.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testNonIntegerFails()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, 1, 100, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"count", "Count", "integer", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "abc");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("valid integer");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test invalid regex pattern in validation.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testInvalidPatternReportsError()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
"[invalid", null, null, null, null, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "test");
|
||||
|
||||
assertThat(result.valid()).isFalse();
|
||||
assertThat(result.errorMessage()).contains("Invalid validation pattern");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test no validation rules passes.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testNoValidationRulesPasses()
|
||||
{
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"name", "Name", "text", null, null, null, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "anything");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Test number type works same as integer type.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void testNumberTypeValidation()
|
||||
{
|
||||
PromptValidation validation = new PromptValidation(
|
||||
null, null, null, 1, 10, null, null);
|
||||
|
||||
TemplateManifest.Prompt prompt = new TemplateManifest.Prompt(
|
||||
"count", "Count", "number", null, null, validation, false);
|
||||
|
||||
PromptValidator.ValidationResult result = validator.validate(prompt, "5");
|
||||
|
||||
assertThat(result.valid()).isTrue();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user