diff --git a/src/org/labkey/test/TestProperties.java b/src/org/labkey/test/TestProperties.java index 83d56286fc..88fb9ad949 100644 --- a/src/org/labkey/test/TestProperties.java +++ b/src/org/labkey/test/TestProperties.java @@ -257,6 +257,11 @@ public static boolean isTrialServer() return getBooleanProperty("webtest.server.trial", false); } + public static boolean isRemoteNameValidationEnabled() + { + return getBooleanProperty("webtest.remote.domain.validation", false); + } + public static boolean isCheckerFatal() { return "true".equals(System.getProperty("webtest.checker.fatal")); diff --git a/src/org/labkey/test/tests/DomainDesignerTest.java b/src/org/labkey/test/tests/DomainDesignerTest.java index 040e53438b..a8e2e78a7c 100644 --- a/src/org/labkey/test/tests/DomainDesignerTest.java +++ b/src/org/labkey/test/tests/DomainDesignerTest.java @@ -316,7 +316,7 @@ public void testInvalidLookupDomainField() throws IOException, CommandException public void testInvalidSampleFieldFromDelete() throws Exception { String listName = TestDataGenerator.randomDomainName("Sample Lookups List", DomainUtils.DomainKind.IntList); - String listKey = TestDataGenerator.randomFieldName("Id", null, 10, null, DomainUtils.DomainKind.IntList); + String listKey = DomainUtils.DomainKind.IntList.randomFieldName("Key"); String sampleType1 = TestDataGenerator.randomDomainName("Sample Type 1"); String sampleType2 = TestDataGenerator.randomDomainName("Sample Type 2"); SampleTypeAPIHelper.createEmptySampleType(getProjectName(), new SampleTypeDefinition(sampleType1)); diff --git a/src/org/labkey/test/tests/query/QueryLookupTest.java b/src/org/labkey/test/tests/query/QueryLookupTest.java index dec685a921..0e1aad74b3 100644 --- a/src/org/labkey/test/tests/query/QueryLookupTest.java +++ b/src/org/labkey/test/tests/query/QueryLookupTest.java @@ -29,7 +29,7 @@ public class QueryLookupTest extends BaseWebDriverTest private static final String PROJECT_NAME = "QueryLookupTest" + TRICKY_CHARACTERS_FOR_PROJECT_NAMES; private static final String LIST_NAME = "l&ist q"; - private static final FieldInfo NAME_COLUMN = FieldInfo.random("Name", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.VarList); + private static final FieldInfo NAME_COLUMN = FieldInfo.random("Key", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.VarList); private static final FieldInfo TSHIRT_COLUMN = FieldInfo.random("TShirt", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.VarList); @Override diff --git a/src/org/labkey/test/util/DomainUtils.java b/src/org/labkey/test/util/DomainUtils.java index 9266495c22..790cd2afa3 100644 --- a/src/org/labkey/test/util/DomainUtils.java +++ b/src/org/labkey/test/util/DomainUtils.java @@ -6,6 +6,8 @@ import org.labkey.remoteapi.domain.DropDomainCommand; import org.labkey.remoteapi.domain.GetDomainDetailsCommand; import org.labkey.test.WebTestHelper; +import org.labkey.test.params.FieldDefinition; +import org.labkey.test.params.FieldInfo; import java.io.IOException; @@ -108,5 +110,20 @@ public String randomName(String namePart) { return TestDataGenerator.randomDomainName(namePart, this); } + + public String randomFieldName(String namePart) + { + return randomField(namePart).getName(); + } + + public FieldInfo randomField(String namePart) + { + return randomField(namePart, null); + } + + public FieldInfo randomField(String namePart, FieldDefinition.ColumnType columnType) + { + return FieldInfo.random(namePart, columnType, this); + } } } diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index 27f0f41cfb..e53ac7b0aa 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -37,8 +37,10 @@ import org.labkey.remoteapi.query.SelectRowsResponse; import org.labkey.remoteapi.query.Sort; import org.labkey.serverapi.reader.TabLoader; +import org.labkey.test.TestProperties; import org.labkey.test.WebTestHelper; import org.labkey.test.params.FieldDefinition; +import org.labkey.test.util.DomainUtils.DomainKind; import org.labkey.test.util.data.ColumnNameMapper; import org.labkey.test.util.data.TestDataUtils; import org.labkey.test.util.query.QueryApiHelper; @@ -566,7 +568,7 @@ public static String randomInvalidDomainName(@Nullable String namePart, int numS return domainName; } - public static String randomDomainName(@Nullable String namePart, @Nullable DomainUtils.DomainKind domainKind) + public static String randomDomainName(@Nullable String namePart, @Nullable DomainKind domainKind) { return randomDomainName(namePart, null, null, domainKind); } @@ -579,10 +581,10 @@ public static String randomDomainName(@Nullable String namePart, @Nullable Domai * @param numEndChars Number of random characters at end of name * @return name containing the given name part and appended random characters that should be a valid domain name */ - public static String randomDomainName(@Nullable String namePart, @Nullable Integer numStartChars, @Nullable Integer numEndChars, @Nullable DomainUtils.DomainKind domainKind) + public static String randomDomainName(@Nullable String namePart, @Nullable Integer numStartChars, @Nullable Integer numEndChars, @Nullable DomainKind domainKind) { String _namePart = namePart == null ? "" : namePart; - DomainUtils.DomainKind _domainKind = domainKind == null ? DomainUtils.DomainKind.SampleSet : domainKind; + DomainKind _domainKind = domainKind == null ? DomainKind.SampleSet : domainKind; String charSet = ALPHANUMERIC_STRING + DOMAIN_SPECIAL_STRING; int currentTries = 0; String domainName = randomName(_namePart, getNumChars(numStartChars, 5), getNumChars(numEndChars, 50), charSet, null); @@ -615,14 +617,14 @@ public static String randomFieldName(String part, @Nullable String exclusion) return randomFieldName(part, exclusion, null); } - public static String randomFieldName(String part, @Nullable String exclusion, DomainUtils.DomainKind domainKind) + public static String randomFieldName(String part, @Nullable String exclusion, DomainKind domainKind) { return randomFieldName(part, null, null, exclusion, domainKind); } - public static String randomFieldName(@NotNull String part, @Nullable Integer numStartChars, @Nullable Integer numEndChars, @Nullable String exclusion, @Nullable DomainUtils.DomainKind domainKind) + public static String randomFieldName(@NotNull String part, @Nullable Integer numStartChars, @Nullable Integer numEndChars, @Nullable String exclusion, @Nullable DomainKind domainKind) { - DomainUtils.DomainKind _domainKind = domainKind == null ? DomainUtils.DomainKind.SampleSet : domainKind; + DomainKind _domainKind = domainKind == null ? DomainKind.SampleSet : domainKind; // use the characters that we know are encoded in fieldKeys plus characters that we know clients are using // Issue 53197: Field name with double byte character can cause client side exception in Firefox when trying to customize grid view. @@ -642,7 +644,22 @@ public static String randomFieldName(@NotNull String part, @Nullable Integer num return randomFieldName; } - private static boolean isDomainAndFieldNameInvalid(DomainUtils.DomainKind domainKind, @Nullable String domainName, @Nullable String fieldName) + private static boolean isDomainAndFieldNameInvalid(DomainKind domainKind, @Nullable String domainName, @Nullable String fieldName) + { + if (fieldName != null && fieldName.length() > 64 && fieldName.toLowerCase().contains("key")) // Not guaranteed but likely a list key + return true; // Issue 53706: List key field name length is limited to 64 characters + + if (TestProperties.isRemoteNameValidationEnabled()) + { + return isNameInvalidRemote(domainKind, domainName, fieldName); + } + else + { + return isNameInvalidLocal(domainKind, domainName, fieldName); + } + } + + private static boolean isNameInvalidRemote(DomainKind domainKind, @Nullable String domainName, @Nullable String fieldName) { SimplePostCommand command = new SimplePostCommand("property", "validateDomainAndFieldNames"); JSONObject domainDesign = new JSONObject(); @@ -679,6 +696,35 @@ private static boolean isDomainAndFieldNameInvalid(DomainUtils.DomainKind domain } } + public static boolean isNameInvalidLocal(DomainKind domainKind, @Nullable String domainName, @Nullable String fieldName) + { + if (domainName != null) + { + if (!Character.isLetterOrDigit(domainName.charAt(0))) + return true; // domain needs to start with alphanumeric char + if (Pattern.matches("(.*\\s--[^ ].*)|(.*\\s-[^- ].*)", domainName)) + return true; // domain name must not contain space followed by dash. (command like: Issue 49161) + + int maxLength = switch (domainKind) + { + case Assay -> 200 - 13; // Make room for "{$domainName} Batch Fields" domain + case SampleSet -> 100; + default -> 200; // Sources, lists, and datasets allow 200 character names + }; + if (domainName.length() > maxLength) + return true; + } + if (fieldName != null) + { + if (fieldName.length() > 200) + return true; + if (Pattern.matches(".*:[a-zA-Z]{3}.*", fieldName)) // Avoid illegal patterns like ":Date" + return true; + } + + return false; + } + public static T randomChoice(List choices) { return choices.get(randomInt(0, choices.size() - 1));