From 7b8d07954b5513eda46b270e7b29794cfe2e159b Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 5 Nov 2025 14:43:59 -0500 Subject: [PATCH] Remove more old-console-related files (#2866) --- .../registry/module/RegistryComponent.java | 2 - .../module/frontend/FrontendComponent.java | 2 - .../java/google/registry/ui/ConsoleDebug.java | 60 -- .../registry/ui/forms/FormException.java | 58 -- .../google/registry/ui/forms/FormField.java | 762 ------------------ .../registry/ui/forms/FormFieldException.java | 178 ---- .../google/registry/ui/forms/FormFields.java | 116 --- .../registry/ui/forms/package-info.java | 17 - .../java/google/registry/ui/html/error.html | 15 - .../java/google/registry/ui/html/index.html | 6 - .../java/google/registry/ui/package-info.java | 16 - .../google/registry/ui/server/StateCode.java | 82 -- .../console/settings/ContactAction.java | 7 +- .../registry/ui/server/package-info.java | 16 - .../frontend/FrontendTestComponent.java | 2 - .../registry/server/RegistryTestServer.java | 6 - .../server/RegistryTestServerMain.java | 17 +- .../ui/forms/FormFieldExceptionTest.java | 62 -- .../registry/ui/forms/FormFieldTest.java | 486 ----------- .../registry/ui/forms/FormFieldsTest.java | 108 --- 20 files changed, 6 insertions(+), 2012 deletions(-) delete mode 100644 core/src/main/java/google/registry/ui/ConsoleDebug.java delete mode 100644 core/src/main/java/google/registry/ui/forms/FormException.java delete mode 100644 core/src/main/java/google/registry/ui/forms/FormField.java delete mode 100644 core/src/main/java/google/registry/ui/forms/FormFieldException.java delete mode 100644 core/src/main/java/google/registry/ui/forms/FormFields.java delete mode 100644 core/src/main/java/google/registry/ui/forms/package-info.java delete mode 100644 core/src/main/java/google/registry/ui/html/error.html delete mode 100644 core/src/main/java/google/registry/ui/html/index.html delete mode 100644 core/src/main/java/google/registry/ui/package-info.java delete mode 100644 core/src/main/java/google/registry/ui/server/StateCode.java delete mode 100644 core/src/main/java/google/registry/ui/server/package-info.java delete mode 100644 core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java delete mode 100644 core/src/test/java/google/registry/ui/forms/FormFieldTest.java delete mode 100644 core/src/test/java/google/registry/ui/forms/FormFieldsTest.java diff --git a/core/src/main/java/google/registry/module/RegistryComponent.java b/core/src/main/java/google/registry/module/RegistryComponent.java index d41b3b262..f8b00fe86 100644 --- a/core/src/main/java/google/registry/module/RegistryComponent.java +++ b/core/src/main/java/google/registry/module/RegistryComponent.java @@ -49,7 +49,6 @@ import google.registry.request.Modules.UrlConnectionServiceModule; import google.registry.request.RequestHandler; import google.registry.request.auth.AuthModule; import google.registry.request.auth.RequestAuthenticator; -import google.registry.ui.ConsoleDebug.ConsoleConfigModule; import google.registry.util.UtilsModule; import jakarta.inject.Provider; import jakarta.inject.Singleton; @@ -63,7 +62,6 @@ import jakarta.inject.Singleton; BigqueryModule.class, CloudTasksUtilsModule.class, ConfigModule.class, - ConsoleConfigModule.class, CredentialModule.class, CustomLogicFactoryModule.class, DirectoryModule.class, diff --git a/core/src/main/java/google/registry/module/frontend/FrontendComponent.java b/core/src/main/java/google/registry/module/frontend/FrontendComponent.java index 10515a91a..4a8c7dc27 100644 --- a/core/src/main/java/google/registry/module/frontend/FrontendComponent.java +++ b/core/src/main/java/google/registry/module/frontend/FrontendComponent.java @@ -35,7 +35,6 @@ import google.registry.privileges.secretmanager.SecretManagerModule; import google.registry.request.Modules.GsonModule; import google.registry.request.Modules.NetHttpTransportModule; import google.registry.request.auth.AuthModule; -import google.registry.ui.ConsoleDebug.ConsoleConfigModule; import google.registry.util.UtilsModule; import jakarta.inject.Singleton; @@ -46,7 +45,6 @@ import jakarta.inject.Singleton; AuthModule.class, CloudTasksUtilsModule.class, ConfigModule.class, - ConsoleConfigModule.class, CredentialModule.class, CustomLogicFactoryModule.class, CloudTasksUtilsModule.class, diff --git a/core/src/main/java/google/registry/ui/ConsoleDebug.java b/core/src/main/java/google/registry/ui/ConsoleDebug.java deleted file mode 100644 index 31e748c42..000000000 --- a/core/src/main/java/google/registry/ui/ConsoleDebug.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui; - -import dagger.Module; -import dagger.Provides; - -/** Enum defining which JS/CSS files get rendered in a soy templates. */ -public enum ConsoleDebug { - - /** Use compiled CSS and JS. */ - PRODUCTION, - - /** Use debug compiled CSS and JS, where symbols are only slightly mangled. */ - DEBUG, - - /** - * Use debug compiled CSS and raw JS from internal source code dependency-managed directory - * structure. - */ - RAW, - - /** Don't use any CSS or JS. This is used by JSTD unit tests. */ - TEST; - - private static final String PROPERTY = "console.debug"; - private static final String DEFAULT = PRODUCTION.name(); - - /** Returns value configured by system property {@code #PROPERTY}. */ - public static ConsoleDebug get() { - return valueOf(System.getProperty(PROPERTY, DEFAULT)); - } - - /** Sets the global {@link ConsoleDebug} state. */ - public static void set(ConsoleDebug value) { - System.setProperty(PROPERTY, value.toString()); - } - - /** Dagger module for ConsoleDebug. */ - @Module - public static final class ConsoleConfigModule { - - @Provides - static ConsoleDebug provideConsoleDebug() { - return ConsoleDebug.get(); - } - } -} diff --git a/core/src/main/java/google/registry/ui/forms/FormException.java b/core/src/main/java/google/registry/ui/forms/FormException.java deleted file mode 100644 index 91581345d..000000000 --- a/core/src/main/java/google/registry/ui/forms/FormException.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.base.Preconditions.checkNotNull; - -import javax.annotation.Detainted; -import javax.annotation.concurrent.NotThreadSafe; - -/** - * Exception thrown when a form is invalid. Problems with a specific - * form field should use {@link FormFieldException} instead. - * - *

You can safely throw {@code FormException} from within your form - * validator, and the message will automatically be propagated to the - * client form interface. - */ -@NotThreadSafe -public class FormException extends RuntimeException { - - /** - * Creates a new {@link FormException} - * - * @param userMessage should be a friendly message that's safe to show to the user. - */ - public FormException(@Detainted String userMessage) { - super(checkNotNull(userMessage, "userMessage"), null); - } - - /** - * Creates a new {@link FormException} - * - * @param userMessage should be a friendly message that's safe to show to the user. - * @param cause the original cause of this exception. May be null. - */ - public FormException(@Detainted String userMessage, Throwable cause) { - super(checkNotNull(userMessage, "userMessage"), cause); - } - - /** Returns an error message that's safe to display to the user. */ - @Override - @Detainted - public String getMessage() { - return super.getMessage(); - } -} diff --git a/core/src/main/java/google/registry/ui/forms/FormField.java b/core/src/main/java/google/registry/ui/forms/FormField.java deleted file mode 100644 index 37ab2f7b7..000000000 --- a/core/src/main/java/google/registry/ui/forms/FormField.java +++ /dev/null @@ -1,762 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; - -import com.google.common.base.Ascii; -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Range; -import com.google.common.collect.Streams; -import com.google.re2j.Pattern; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import javax.annotation.Detainted; -import javax.annotation.Nullable; -import javax.annotation.Tainted; -import javax.annotation.concurrent.Immutable; - -/** - * Declarative functional fluent form field converter / validator. - * - *

This class is responsible for converting arbitrary data, sent to us by the web browser, into - * validated data structures that the server-side code can use. For example: - * - *

{@code
- * private enum Gender { MALE, FEMALE }
- *
- * private static final FormField NAME_FIELD = FormField.named("name")
- *     .matches("[a-z]+")
- *     .range(atMost(16))
- *     .required()
- *     .build();
- *
- * private static final FormField GENDER_FIELD = FormField.named("gender")
- *     .asEnum(Gender.class)
- *     .required()
- *     .build();
- *
- * public Person makePerson(Map params) {
- *   Person.Builder person = new Person.Builder();
- *   for (String name : NAME_FIELD.extract(params).asSet()) {
- *     person.setName(name);
- *   }
- *   for (Gender name : GENDER_FIELD.extract(params).asSet()) {
- *     person.setGender(name);
- *   }
- *   return person.build();
- * }
- * }
- * - *

This class provides full type-safety if and only if you statically initialize - * your FormField objects and write a unit test that causes the class to be loaded. - * - *

Exception Handling

- * - *

When values passed to {@link #convert} or {@link #extract} don't meet the contract, {@link - * FormFieldException} will be thrown, which provides the field name and a short error message - * that's safe to pass along to the client. - * - *

You can safely throw {@code FormFieldException} from within your validator functions, and the - * field name will automatically be propagated into the exception object for you. - * - *

In situations when you're validating lists or maps, you'll end up with a hierarchical field - * naming structure. For example, if you were validating a list of maps, an error generated by the - * {@code bar} field of the fifth item in the {@code foo} field would have a fully-qualified field - * name of: {@code foo[5][bar]}. - * - *

Library Definitions

- * - *

You should never assign a partially constructed {@code FormField.Builder} to a variable or - * constant. Instead, you should use {@link #asBuilder()} or {@link #asBuilderNamed(String)}. - * - *

Here is an example of how you might go about defining library definitions: - * - *

{@code
- * final class FormFields {
- *   private static final FormField COUNTRY_CODE =
- *       FormField.named("countryCode")
- *           .range(Range.singleton(2))
- *           .uppercased()
- *           .in(ImmutableSet.copyOf(Locale.getISOCountries()))
- *           .build();
- * }
- *
- * final class Form {
- *   private static final FormField COUNTRY_CODE_FIELD =
- *       FormFields.COUNTRY_CODE.asBuilder()
- *           .required()
- *           .build();
- * }
- * }
- * - * @param input value type - * @param output value type - */ -@Immutable -public final class FormField { - - private final String name; - private final Class typeIn; - private final Class typeOut; - private final Function converter; - - private FormField(String name, Class typeIn, Class typeOut, Function converter) { - this.name = name; - this.typeIn = typeIn; - this.typeOut = typeOut; - this.converter = converter; - } - - /** Returns an optional string form field named {@code name}. */ - public static Builder named(String name) { - return named(name, String.class); - } - - /** Returns an optional form field named {@code name} with a specific {@code inputType}. */ - public static Builder named(String name, Class typeIn) { - checkArgument(!name.isEmpty()); - return new Builder<>(name, checkNotNull(typeIn), typeIn, x -> x); - } - - /** - * Returns a form field builder for validating JSON nested maps. - * - *

Here's an example of how you'd use this feature: - * - *

-   *   private static final FormField<String, String> REGISTRAR_NAME_FIELD =
-   *       FormField.named("name")
-   *           .emptyToNull()
-   *           .required()
-   *           .build();
-   *
-   *   private static final FormField<Map<String, ?>, Registrar> REGISTRAR_FIELD =
-   *       FormField.mapNamed("registrar")
-   *           .transform(Registrar.class, new Function<Map<String, ?>, Registrar>() {
-   *             @Nullable
-   *             @Override
-   *             public Registrar apply(@Nullable Map<String, ?> params) {
-   *               Registrar.Builder builder = new Registrar.Builder();
-   *               for (String name : REGISTRAR_NAME_FIELD.extractUntyped(params).asSet()) {
-   *                builder.setName(name);
-   *               }
-   *               return builder.build();
-   *             }})
-   *           .build();
- * - *

When a {@link FormFieldException} is thrown, it'll be propagated to create a fully-qualified - * field name. For example, if the JSON input is

{registrar: {name: ""}}
then the - * {@link FormFieldException#getFieldName() field name} will be {@code registrar.name}. - */ - public static Builder, Map> mapNamed(String name) { - @SuppressWarnings("unchecked") - Class> typeIn = (Class>) (Class) Map.class; - return named(name, typeIn); - } - - /** Returns the name of this field. */ - public String name() { - return name; - } - - /** - * Convert and validate a raw user-supplied value. - * - * @throws FormFieldException if value does not meet expected contracts. - */ - @Detainted - public Optional convert(@Tainted @Nullable I value) { - try { - return Optional.ofNullable(converter.apply(value)); - } catch (FormFieldException e) { - throw e.propagate(name); - } - } - - /** - * Convert and validate a raw user-supplied value from a map. - * - *

This is the same as saying: {@code field.convert(valueMap.get(field.name())} - * - * @throws FormFieldException if value does not meet expected contracts. - */ - @Detainted - public Optional extract(@Tainted Map valueMap) { - return convert(valueMap.get(name)); - } - - /** - * Convert and validate a raw user-supplied value from an untyped JSON map. - * - * @throws FormFieldException if value is wrong type or does not meet expected contracts. - */ - @Detainted - public Optional extractUntyped(@Tainted Map jsonMap) { - Object value = jsonMap.get(name); - I castedValue; - try { - castedValue = typeIn.cast(value); - } catch (ClassCastException e) { - throw new FormFieldException(String.format("Type error: got: %s, expected: %s", - value.getClass().getSimpleName(), - typeIn.getSimpleName())).propagate(name); - } - return convert(castedValue); - } - - /** - * Returns a builder of this object, which can be used to further restrict validation. - * - * @see #asBuilderNamed(String) - */ - public Builder asBuilder() { - return new Builder<>(name, typeIn, typeOut, converter); - } - - /** Same as {@link #asBuilder()} but changes the field name. */ - public Builder asBuilderNamed(String newName) { - checkArgument(!newName.isEmpty()); - return new Builder<>(newName, typeIn, typeOut, converter); - } - - /** - * Mutable builder for {@link FormField}. - * - * @param input value type - * @param output value type - */ - public static final class Builder { - private final String name; - private final Class typeIn; - private final Class typeOut; - private Function converter; - - private Builder(String name, Class typeIn, Class typeOut, Function converter) { - this.name = name; - this.typeIn = typeIn; - this.typeOut = typeOut; - this.converter = converter; - } - - /** Causes {@code defaultValue} to be substituted if value is {@code null}. */ - public Builder withDefault(O defaultValue) { - return transform(new DefaultFunction<>(checkNotNull(defaultValue))); - } - - /** Ensure value is not {@code null}. */ - public Builder required() { - return transform(Builder::checkNotNullTransform); - } - - /** - * Transform empty values into {@code null}. - * - * @throws IllegalStateException if current output type is not a {@link CharSequence} or - * {@link Collection}. - */ - public Builder emptyToNull() { - checkState(CharSequence.class.isAssignableFrom(typeOut) - || Collection.class.isAssignableFrom(typeOut)); - return transform( - input -> - ((input instanceof CharSequence) && (((CharSequence) input).length() == 0)) - || ((input instanceof Collection) && ((Collection) input).isEmpty()) - ? null - : input); - } - - /** - * Modify {@link String} input to remove whitespace around the sides. - * - *

{@code null} values are passed through. - * - * @throws IllegalStateException if current output type is not a String. - */ - public Builder trimmed() { - checkState(String.class.isAssignableFrom(typeOut)); - @SuppressWarnings("unchecked") - Function trimFunction = - (Function) - ((Function) input -> input != null ? input.trim() : null); - return transform(String.class, trimFunction); - } - - /** - * Modify {@link String} input to be uppercase. - * - *

{@code null} values are passed through. - * - * @throws IllegalStateException if current output type is not a String. - */ - public Builder uppercased() { - checkState(String.class.isAssignableFrom(typeOut)); - @SuppressWarnings("unchecked") - Function funk = - (Function) - ((Function) - input -> input != null ? input.toUpperCase(Locale.ENGLISH) : null); - return transform(String.class, funk); - } - - /** - * Modify {@link String} input to be lowercase. - * - *

{@code null} values are passed through. - * - * @throws IllegalStateException if current output type is not a String. - */ - public Builder lowercased() { - checkState(String.class.isAssignableFrom(typeOut)); - @SuppressWarnings("unchecked") - Function funk = - (Function) - ((Function) - input -> input != null ? input.toLowerCase(Locale.ENGLISH) : null); - return transform(String.class, funk); - } - - /** - * Ensure input matches {@code pattern}. - * - *

{@code null} values are passed through. - * - * @param pattern is used to validate the user input. It matches against the whole string, so - * you don't need to use the ^$ characters. - * @param errorMessage is a helpful error message, which should include an example. If this is - * not provided, a default error message will be shown that includes the regexp pattern. - * @throws IllegalStateException if current output type is not a {@link CharSequence}. - * @see #matches(Pattern) - */ - public Builder matches(Pattern pattern, @Nullable String errorMessage) { - checkState(CharSequence.class.isAssignableFrom(typeOut)); - return transform( - new MatchesFunction<>(checkNotNull(pattern), Optional.ofNullable(errorMessage))); - } - - /** Alias for {@link #matches(Pattern, String) matches(pattern, null)} */ - public Builder matches(Pattern pattern) { - return matches(pattern, null); - } - - /** - * Removes all characters not in {@code matcher}. - * - *

{@code null} values are passed through. - * - * @param matcher indicates which characters are to be retained - * @throws IllegalStateException if current output type is not a {@link CharSequence} - */ - public Builder retains(CharMatcher matcher) { - checkState(CharSequence.class.isAssignableFrom(typeOut)); - @SuppressWarnings("unchecked") // safe due to checkState call - Function function = - (Function) new RetainFunction(checkNotNull(matcher)); - return transform(String.class, function); - } - - /** - * Enforce value length/size/value is within {@code range}. - * - *

The following input value types are supported: - * - *

    - *
  • {@link CharSequence}: Length must be within {@code range}. - *
  • {@link Collection}: Size must be within {@code range}. - *
  • {@link Number}: Value must be within {@code range}. - *
- * - *

{@code null} values are passed through. Please note that setting a lower bound on your - * range does not imply {@link #required()}, as range checking only applies to non-{@code null} - * values. - * - * @throws IllegalStateException if current output type is not one of the above types. - */ - public Builder range(Range range) { - checkState(CharSequence.class.isAssignableFrom(typeOut) - || Collection.class.isAssignableFrom(typeOut) - || Number.class.isAssignableFrom(typeOut)); - return transform(new RangeFunction<>(checkNotNull(range))); - } - - /** - * Enforce value be a member of {@code values}. - * - *

{@code null} values are passed through. - * - * @throws IllegalArgumentException if {@code values} is empty. - */ - public Builder in(Set values) { - checkArgument(!values.isEmpty()); - return transform(new InFunction<>(values)); - } - - /** - * Performs arbitrary type transformation from {@code O} to {@code T}. - * - *

Your {@code transform} function is expected to pass-through {@code null} values as a - * no-op, since it's up to {@link #required()} to block them. You might also want to consider - * using a try block that rethrows exceptions as {@link FormFieldException}. - * - *

Here's an example of how you'd convert from String to Integer: - * - *

-     *   FormField.named("foo", String.class)
-     *       .transform(Integer.class, new Function<String, Integer>() {
-     *         @Nullable
-     *         @Override
-     *         public Integer apply(@Nullable String input) {
-     *           try {
-     *             return input != null ? Integer.parseInt(input) : null;
-     *           } catch (IllegalArgumentException e) {
-     *             throw new FormFieldException("Invalid number.", e);
-     *           }
-     *         }})
-     *       .build();
- * - * @see #transform(Function) - */ - public Builder transform(Class newType, Function transform) { - return new Builder<>( - name, typeIn, checkNotNull(newType), this.converter.andThen(checkNotNull(transform))); - } - - /** - * Manipulates values without changing type. - * - *

Please see {@link #transform(Class, Function)} for information about the contract to - * which {@code transform} is expected to conform. - */ - public Builder transform(Function transform) { - this.converter = this.converter.andThen(checkNotNull(transform)); - return this; - } - - /** - * Uppercases value and converts to an enum field of {@code enumClass}. - * - *

{@code null} values are passed through. - * - * @throws IllegalArgumentException if {@code enumClass} is not an enum class. - * @throws IllegalStateException if current output type is not a String. - */ - public > Builder asEnum(Class enumClass) { - checkArgument(enumClass.isEnum()); - checkState(String.class.isAssignableFrom(typeOut)); - return transform(enumClass, new ToEnumFunction<>(enumClass)); - } - - /** - * Turns this form field into something that processes lists. - * - *

The current object definition will be applied to each item in the list. If a - * {@link FormFieldException} is thrown when processing an item, then its - * {@link FormFieldException#getFieldName() fieldName} will be rewritten to include the index, - * e.g. {@code name} becomes {@code name[0]}. - * - *

The outputted list will be an {@link ImmutableList}. This is not reflected in the generic - * typing for the sake of brevity. - * - *

A {@code null} value for list will be passed through. List items that convert to - * {@code null} will be discarded (since {@code ImmutableList} does not permit {@code null} - * values). - */ - public Builder, List> asList() { - @SuppressWarnings("unchecked") Class> in = (Class>) (Class) List.class; - @SuppressWarnings("unchecked") Class> out = (Class>) (Class) List.class; - return new Builder<>(name, in, out, new ToListFunction<>(build())); - } - - /** - * Turns this form field into a split string list that applies itself to each item. - * - *

The behavior of this method is counter-intuitive. It behaves similar to {@link #asList()} - * in the sense that all transforms specified before this method will be applied to the - * individual resulting list items. - * - *

For example, to turn a comma-delimited string into an enum list:

   {@code
-     *
-     *   private static final FormField> STATES_FIELD =
-     *       FormField.named("states")
-     *            .uppercased()
-     *            .asEnum(State.class)
-     *            .asList(Splitter.on(',').omitEmptyStrings().trimResults())
-     *            .build();}
- * - *

You'll notice that the transforms specified before this method are applied to each list - * item. However unlike {@link #asList()}, if an error is thrown on an individual item, then - * {@link FormFieldException#getFieldName()} will not contain the index. - * - * @throws IllegalStateException If either the current input type isn't String. - */ - public Builder> asList(Splitter splitter) { - checkNotNull(splitter); - checkState(String.class.isAssignableFrom(typeIn)); - @SuppressWarnings("unchecked") Class> out = (Class>) (Class) List.class; - @SuppressWarnings("unchecked") FormField inField = (FormField) build(); - return new Builder<>(name, String.class, out, new SplitToListFunction<>(inField, splitter)); - } - - /** - * Same as {@link #asList()} but outputs an {@link ImmutableSet} instead. - * - * @throws IllegalStateException if you called asList() before calling this method. - */ - public Builder, Set> asSet() { - checkState(!List.class.isAssignableFrom(typeOut)); - @SuppressWarnings("unchecked") - Class> setOut = (Class>) (Class) Set.class; - @SuppressWarnings("unchecked") - Function, Set> toSetFunction = - (Function, Set>) - (Function) - ((Function, Set>) - input -> input != null ? ImmutableSet.copyOf(input) : null); - return asList().transform(setOut, toSetFunction); - } - - /** - * Same as {@link #asList(Splitter)} but outputs an {@link ImmutableSet} instead. - * - * @throws IllegalStateException If the current input type isn't String. - */ - public Builder> asSet(Splitter splitter) { - checkNotNull(splitter); - checkState(String.class.isAssignableFrom(typeIn)); - @SuppressWarnings("unchecked") Class> out = (Class>) (Class) Set.class; - @SuppressWarnings("unchecked") FormField inField = (FormField) build(); - return new Builder<>(name, String.class, out, new SplitToSetFunction<>(inField, splitter)); - } - - /** Creates a new {@link FormField} instance. */ - public FormField build() { - return new FormField<>(name, typeIn, typeOut, converter); - } - - private static O checkNotNullTransform(@Nullable O input) { - if (input == null) { - throw new FormFieldException("This field is required."); - } - return input; - } - - private static final class DefaultFunction implements Function { - private final O defaultValue; - - DefaultFunction(O defaultValue) { - this.defaultValue = defaultValue; - } - - @Nullable - @Override - public O apply(@Nullable O input) { - return input != null ? input : defaultValue; - } - } - - private static final class RangeFunction implements Function { - private final Range range; - - RangeFunction(Range range) { - this.range = range; - } - - @Nullable - @Override - public O apply(@Nullable O input) { - if (input == null) { - return null; - } - if (input instanceof CharSequence) { - checkRangeContains(range, ((CharSequence) input).length(), "Number of characters"); - } else if (input instanceof Collection) { - checkRangeContains(range, ((Collection) input).size(), "Number of items"); - } else if (input instanceof Number) { - checkRangeContains(range, ((Number) input).intValue(), "Value"); - } else { - throw new AssertionError(); - } - return input; - } - - private void checkRangeContains(Range range, int value, String message) { - if (!range.contains(value)) { - throw new FormFieldException( - String.format("%s (%,d) not in range %s", message, value, range)); - } - } - } - - private static final class InFunction implements Function { - private final Set values; - - InFunction(Set values) { - this.values = values; - } - - @Nullable - @Override - public O apply(@Nullable O input) { - if (input == null) { - return null; - } - if (!values.contains(input)) { - throw new FormFieldException("Unrecognized value."); - } - return input; - } - } - - private static final class MatchesFunction implements Function { - private final Pattern pattern; - private final Optional errorMessage; - - MatchesFunction(Pattern pattern, Optional errorMessage) { - this.pattern = pattern; - this.errorMessage = errorMessage; - } - - @Nullable - @Override - public O apply(@Nullable O input) { - if (input == null) { - return null; - } - if (!pattern.matcher((CharSequence) input).matches()) { - throw new FormFieldException(errorMessage.orElse("Must match pattern: " + pattern)); - } - return input; - } - } - - private static final class RetainFunction implements Function { - private final CharMatcher matcher; - - RetainFunction(CharMatcher matcher) { - this.matcher = matcher; - } - - @Nullable - @Override - public String apply(@Nullable CharSequence input) { - if (input == null) { - return null; - } - return matcher.retainFrom(input); - } - } - - private static final class ToEnumFunction> implements Function { - private final Class enumClass; - - ToEnumFunction(Class enumClass) { - this.enumClass = enumClass; - } - - @Nullable - @Override - public C apply(@Nullable O input) { - try { - return input != null ? Enum.valueOf(enumClass, Ascii.toUpperCase((String) input)) : null; - } catch (IllegalArgumentException e) { - throw new FormFieldException( - String.format("Enum %s does not contain '%s'", enumClass.getSimpleName(), input)); - } - } - } - - private static final class ToListFunction implements Function, List> { - private final FormField itemField; - - ToListFunction(FormField itemField) { - this.itemField = itemField; - } - - @Nullable - @Override - public List apply(@Nullable List input) { - if (input == null) { - return null; - } - ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (int i = 0; i < input.size(); i++) { - I inputItem = itemField.typeIn.cast(input.get(i)); - O outputItem; - try { - outputItem = itemField.converter.apply(inputItem); - } catch (FormFieldException e) { - throw e.propagate(i); - } - if (outputItem != null) { - builder.add(outputItem); - } - } - return builder.build(); - } - } - - private static final class SplitToListFunction implements Function> { - private final FormField itemField; - private final Splitter splitter; - - SplitToListFunction(FormField itemField, Splitter splitter) { - this.itemField = itemField; - this.splitter = splitter; - } - - @Nullable - @Override - public List apply(@Nullable String input) { - return input == null - ? null - : Streams.stream(splitter.split(input)) - .map(itemField.converter) - .collect(toImmutableList()); - } - } - - private static final class SplitToSetFunction implements Function> { - private final FormField itemField; - private final Splitter splitter; - - SplitToSetFunction(FormField itemField, Splitter splitter) { - this.itemField = itemField; - this.splitter = splitter; - } - - @Nullable - @Override - public Set apply(@Nullable String input) { - return input == null - ? null - : Streams.stream(splitter.split(input)) - .map(itemField.converter) - .collect(toImmutableSet()); - } - } - } -} diff --git a/core/src/main/java/google/registry/ui/forms/FormFieldException.java b/core/src/main/java/google/registry/ui/forms/FormFieldException.java deleted file mode 100644 index d59959405..000000000 --- a/core/src/main/java/google/registry/ui/forms/FormFieldException.java +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.collect.Lists; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import javax.annotation.CheckReturnValue; -import javax.annotation.Detainted; -import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; - -/** - * Exception thrown when a form field contains a bad value. - * - *

You can safely throw {@code FormFieldException} from within your validator functions, and the - * field name will automatically be propagated into the exception object for you. - * - *

The way that field names work is a bit complicated, because we need to support complex nested - * field names like {@code foo[3].bar}. So what happens is the original exception will be thrown by - * a {@link FormField} validator without the field set. Then as the exception bubbles up the stack, - * it'll be caught by the {@link FormField#convert(Object) convert} method, which then prepends the - * name of that component. Then when the exception reaches the user, the {@link #getFieldName()} - * method will produce the fully-qualified field name. - * - *

This propagation mechanism is also very important when writing {@link - * FormField.Builder#transform} functions, which oftentimes will not know the name of the field - * they're validating. - */ -@NotThreadSafe -@SuppressWarnings("OverrideThrowableToString") -public final class FormFieldException extends FormException { - - private final List names = new ArrayList<>(); - - @Nullable - private String lazyFieldName; - - /** - * Creates a new {@link FormFieldException} - * - *

This exception should only be thrown from within a {@link FormField} converter function. - * The field name will automatically be propagated into the exception object for you. - * - * @param userMessage should be a friendly message that's safe to show to the user. - */ - public FormFieldException(@Detainted String userMessage) { - super(checkNotNull(userMessage, "userMessage"), null); - } - - /** - * Creates a new {@link FormFieldException} - * - *

This exception should only be thrown from within a {@link FormField} converter function. - * The field name will automatically be propagated into the exception object for you. - * - * @param userMessage should be a friendly message that's safe to show to the user. - * @param cause the original cause of this exception (non-null). - */ - public FormFieldException(@Detainted String userMessage, Throwable cause) { - super(checkNotNull(userMessage, "userMessage"), checkNotNull(cause, "cause")); - } - - /** - * Creates a new {@link FormFieldException} for a particular form field. - * - *

This exception should only be thrown from within a {@link FormField} MAP converter function - * in situations where you're performing additional manual validation. - * - * @param userMessage should be a friendly message that's safe to show to the user. - */ - public FormFieldException(FormField field, @Detainted String userMessage) { - this(field.name(), userMessage); - } - - /** - * Creates a new {@link FormFieldException} for a particular field name. - * - * @param field name corresponding to a {@link FormField#name()} - * @param userMessage friendly message that's safe to show to the user - */ - public FormFieldException(String field, @Detainted String userMessage) { - super(checkNotNull(userMessage, "userMessage"), null); - propagateImpl(field); - } - - /** Returns the fully-qualified name (JavaScript syntax) of the form field causing this error. */ - public String getFieldName() { - String fieldName = lazyFieldName; - if (fieldName == null) { - lazyFieldName = fieldName = getFieldNameImpl(); - } - return fieldName; - } - - private String getFieldNameImpl() { - checkState(!names.isEmpty(), - "FormFieldException was thrown outside FormField infrastructure!"); - Iterator namesIterator = Lists.reverse(names).iterator(); - StringBuilder result = new StringBuilder((String) namesIterator.next()); - while (namesIterator.hasNext()) { - Object name = namesIterator.next(); - if (name instanceof String) { - result.append('.').append(name); - } else if (name instanceof Integer) { - result.append('[').append(name).append(']'); - } else { - throw new AssertionError(); - } - } - return result.toString(); - } - - /** - * Returns self with {@code name} prepended, for propagating exceptions up the stack. - * - *

This would be package-private except that it needs to be called by a test class in another - * package. - */ - @CheckReturnValue - public FormFieldException propagate(String name) { - return propagateImpl(name); - } - - /** Returns self with {@code index} prepended, for propagating exceptions up the stack. */ - @CheckReturnValue - FormFieldException propagate(int index) { - return propagateImpl(index); - } - - /** Returns self with {@code name} prepended, for propagating exceptions up the stack. */ - private FormFieldException propagateImpl(Object name) { - lazyFieldName = null; - names.add(checkNotNull(name)); - return this; - } - - @Override - public boolean equals(@Nullable Object obj) { - return this == obj - || (obj instanceof FormFieldException - && Objects.equals(getCause(), ((FormFieldException) obj).getCause()) - && Objects.equals(getMessage(), ((FormFieldException) obj).getMessage()) - && Objects.equals(names, ((FormFieldException) obj).names)); - } - - @Override - public int hashCode() { - return Objects.hash(getCause(), getMessage(), getFieldName()); - } - - @Override - public String toString() { - return toStringHelper(getClass()) - .add("fieldName", getFieldName()) - .add("message", getMessage()) - .add("cause", getCause()) - .toString(); - } -} diff --git a/core/src/main/java/google/registry/ui/forms/FormFields.java b/core/src/main/java/google/registry/ui/forms/FormFields.java deleted file mode 100644 index 612c03093..000000000 --- a/core/src/main/java/google/registry/ui/forms/FormFields.java +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.collect.Range.atMost; -import static com.google.common.collect.Range.closed; -import static com.google.common.collect.Range.singleton; -import static java.util.Locale.getISOCountries; - -import com.google.common.collect.ImmutableSet; -import com.google.re2j.Pattern; - -/** Utility class of {@link FormField} objects for validating EPP related things. */ -public final class FormFields { - - private static final Pattern WHITESPACE = Pattern.compile("[ \\t\\r\\n]+"); - /** - * Form field that applies XML Schema Token cleanup to input. - * - *

This trims the input and collapses whitespace. - * - * @see XSD Datatypes - token - */ - public static final FormField XS_TOKEN = - FormField.named("xsToken") - .emptyToNull() - .trimmed() - .transform(input -> input != null ? WHITESPACE.matcher(input).replaceAll(" ") : null) - .build(); - - /** - * Form field that ensures input does not contain tabs, line feeds, or carriage returns. - * - * @see - * XSD Datatypes - normalizedString - */ - public static final FormField XS_NORMALIZED_STRING = - FormField.named("xsNormalizedString") - .emptyToNull() - .matches(Pattern.compile("[^\\t\\r\\n]*"), "Must not contain tabs or multiple lines.") - .build(); - - /** - * Form field for +E164 phone numbers with a dot after the country prefix. - * - * @see RFC 5733 - EPP - Formal Syntax - */ - public static final FormField PHONE_NUMBER = - XS_TOKEN.asBuilderNamed("phoneNumber") - .range(atMost(17)) - .matches(Pattern.compile("(\\+[0-9]{1,3}\\.[0-9]{1,14})?"), - "Must be a valid +E.164 phone number, e.g. +1.2125650000") - .build(); - - /** Form field for EPP client identifiers. */ - public static final FormField CLID = XS_TOKEN.asBuilderNamed("clid") - .range(closed(3, 16)) - .build(); - - /** Form field for passwords (see pwType in epp.xsd). */ - public static final FormField PASSWORD = XS_TOKEN.asBuilderNamed("password") - .range(closed(6, 16)) - .build(); - - /** Form field for non-empty tokens (see minToken in eppcom.xsd). */ - public static final FormField MIN_TOKEN = XS_TOKEN.asBuilderNamed("minToken") - .emptyToNull() - .build(); - - /** Form field for nameType (see rde-registrar/notification). */ - public static final FormField NAME = XS_NORMALIZED_STRING.asBuilderNamed("name") - .range(closed(1, 255)) - .build(); - - /** Form field for {@code labelType} from {@code eppcom.xsd}. */ - public static final FormField LABEL = XS_TOKEN.asBuilderNamed("label") - .range(closed(1, 255)) - .build(); - - /** Email address form field. */ - public static final FormField EMAIL = XS_TOKEN.asBuilderNamed("email") - .matches(Pattern.compile("[^@]+@[^@.]+\\.[^@]+"), "Please enter a valid email address.") - .build(); - - /** Two-letter ISO country code form field. */ - public static final FormField COUNTRY_CODE = - XS_TOKEN.asBuilderNamed("countryCode") - .range(singleton(2)) - .uppercased() - .in(ImmutableSet.copyOf(getISOCountries())) - .build(); - - /** - * Ensure value is an EPP Repository Object IDentifier (ROID). - * - * @see Shared Structure Schema - */ - public static final FormField ROID = XS_TOKEN.asBuilderNamed("roid") - .matches(Pattern.compile("(\\w|_){1,80}-\\w{1,8}"), - "Please enter a valid EPP ROID, e.g. SH8013-REP") - .build(); - - private FormFields() {} -} diff --git a/core/src/main/java/google/registry/ui/forms/package-info.java b/core/src/main/java/google/registry/ui/forms/package-info.java deleted file mode 100644 index b01aa93dd..000000000 --- a/core/src/main/java/google/registry/ui/forms/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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. - -/** Web application backend form processing utilities. */ -@javax.annotation.ParametersAreNonnullByDefault -package google.registry.ui.forms; diff --git a/core/src/main/java/google/registry/ui/html/error.html b/core/src/main/java/google/registry/ui/html/error.html deleted file mode 100644 index 44568bb11..000000000 --- a/core/src/main/java/google/registry/ui/html/error.html +++ /dev/null @@ -1,15 +0,0 @@ - - -Server Error - -Google -

500. That's an error. -

Sorry, but the server encountered an error while processing your request. diff --git a/core/src/main/java/google/registry/ui/html/index.html b/core/src/main/java/google/registry/ui/html/index.html deleted file mode 100644 index 9903e3e51..000000000 --- a/core/src/main/java/google/registry/ui/html/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Nomulus - -If this page doesn't change automatically, please go -to https://www.registry.google/console diff --git a/core/src/main/java/google/registry/ui/package-info.java b/core/src/main/java/google/registry/ui/package-info.java deleted file mode 100644 index ddff37c68..000000000 --- a/core/src/main/java/google/registry/ui/package-info.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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. - -@javax.annotation.ParametersAreNonnullByDefault -package google.registry.ui; diff --git a/core/src/main/java/google/registry/ui/server/StateCode.java b/core/src/main/java/google/registry/ui/server/StateCode.java deleted file mode 100644 index 4bf7ef12c..000000000 --- a/core/src/main/java/google/registry/ui/server/StateCode.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.server; - -import com.google.common.collect.ImmutableBiMap; - -/** - * Bimap of state codes and names for the US Regime. - * - * @see State Table - */ -public final class StateCode { - - public static final ImmutableBiMap US_MAP = - new ImmutableBiMap.Builder() - .put("AL", "Alabama") - .put("AK", "Alaska") - .put("AZ", "Arizona") - .put("AR", "Arkansas") - .put("CA", "California") - .put("CO", "Colorado") - .put("CT", "Connecticut") - .put("DE", "Delaware") - .put("FL", "Florida") - .put("GA", "Georgia") - .put("HI", "Hawaii") - .put("ID", "Idaho") - .put("IL", "Illinois") - .put("IN", "Indiana") - .put("IA", "Iowa") - .put("KS", "Kansas") - .put("KY", "Kentucky") - .put("LA", "Louisiana") - .put("ME", "Maine") - .put("MD", "Maryland") - .put("MA", "Massachusetts") - .put("MI", "Michigan") - .put("MN", "Minnesota") - .put("MS", "Mississippi") - .put("MO", "Missouri") - .put("MT", "Montana") - .put("NE", "Nebraska") - .put("NV", "Nevada") - .put("NH", "New Hampshire") - .put("NJ", "New Jersey") - .put("NM", "New Mexico") - .put("NY", "New York") - .put("NC", "North Carolina") - .put("ND", "North Dakota") - .put("OH", "Ohio") - .put("OK", "Oklahoma") - .put("OR", "Oregon") - .put("PA", "Pennsylvania") - .put("RI", "Rhode Island") - .put("SC", "South Carolina") - .put("SD", "South Dakota") - .put("TN", "Tennessee") - .put("TX", "Texas") - .put("UT", "Utah") - .put("VT", "Vermont") - .put("VA", "Virginia") - .put("WA", "Washington") - .put("WV", "West Virginia") - .put("WI", "Wisconsin") - .put("WY", "Wyoming") - .put("DC", "Washington DC") - .build(); - - private StateCode() {} -} diff --git a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java index 72b29db82..1774d8087 100644 --- a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java +++ b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java @@ -40,7 +40,6 @@ import google.registry.request.Action.GaeService; import google.registry.request.Action.GkeService; import google.registry.request.Parameter; import google.registry.request.auth.Auth; -import google.registry.ui.forms.FormException; import google.registry.ui.server.console.ConsoleApiAction; import google.registry.ui.server.console.ConsoleApiParams; import jakarta.inject.Inject; @@ -166,7 +165,7 @@ public class ContactAction extends ConsoleApiAction { try { checkContactRequirements(oldContacts, newContacts); - } catch (FormException e) { + } catch (ContactRequirementException e) { logger.atWarning().withCause(e).log( "Error processing contacts post request for registrar: %s", registrarId); throw new IllegalArgumentException(e); @@ -196,7 +195,7 @@ public class ContactAction extends ConsoleApiAction { /** * Enforces business logic checks on registrar contacts. * - * @throws FormException if the checks fail. + * @throws ContactRequirementException if the checks fail. */ private static void checkContactRequirements( ImmutableSet existingContacts, ImmutableSet updatedContacts) { @@ -299,7 +298,7 @@ public class ContactAction extends ConsoleApiAction { } /** Thrown when a set of contacts doesn't meet certain constraints. */ - private static class ContactRequirementException extends FormException { + private static class ContactRequirementException extends RuntimeException { ContactRequirementException(String msg) { super(msg); } diff --git a/core/src/main/java/google/registry/ui/server/package-info.java b/core/src/main/java/google/registry/ui/server/package-info.java deleted file mode 100644 index d5246ed76..000000000 --- a/core/src/main/java/google/registry/ui/server/package-info.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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. - -@javax.annotation.ParametersAreNonnullByDefault -package google.registry.ui.server; diff --git a/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java b/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java index c238b847e..33ef665b5 100644 --- a/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java +++ b/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java @@ -30,7 +30,6 @@ import google.registry.monitoring.whitebox.StackdriverModule; import google.registry.privileges.secretmanager.SecretManagerModule; import google.registry.request.Modules; import google.registry.request.auth.AuthModule; -import google.registry.ui.ConsoleDebug; import google.registry.util.UtilsModule; import jakarta.inject.Singleton; @@ -40,7 +39,6 @@ import jakarta.inject.Singleton; AuthModule.class, CloudTasksUtilsModule.class, RegistryConfig.ConfigModule.class, - ConsoleDebug.ConsoleConfigModule.class, CredentialModule.class, CustomLogicFactoryModule.class, CloudTasksUtilsModule.class, diff --git a/core/src/test/java/google/registry/server/RegistryTestServer.java b/core/src/test/java/google/registry/server/RegistryTestServer.java index 4439c786e..0a3ccaa6d 100644 --- a/core/src/test/java/google/registry/server/RegistryTestServer.java +++ b/core/src/test/java/google/registry/server/RegistryTestServer.java @@ -35,12 +35,6 @@ public final class RegistryTestServer { public static final ImmutableMap RUNFILES = new ImmutableMap.Builder() - .put( - "/index.html", - PROJECT_ROOT.resolve("core/src/main/java/google/registry/ui/html/index.html")) - .put( - "/error.html", - PROJECT_ROOT.resolve("core/src/main/java/google/registry/ui/html/error.html")) .put("/console/*", PROJECT_ROOT.resolve("console-webapp/staged/dist")) .build(); diff --git a/core/src/test/java/google/registry/server/RegistryTestServerMain.java b/core/src/test/java/google/registry/server/RegistryTestServerMain.java index 437667918..9ef8ca195 100644 --- a/core/src/test/java/google/registry/server/RegistryTestServerMain.java +++ b/core/src/test/java/google/registry/server/RegistryTestServerMain.java @@ -28,7 +28,6 @@ import google.registry.request.auth.AuthResult; import google.registry.request.auth.OidcTokenAuthenticationMechanism; import google.registry.testing.DatabaseHelper; import google.registry.tools.params.HostAndPortParameter; -import google.registry.ui.ConsoleDebug; import java.util.List; /** Command-line interface for {@link RegistryTestServer}. */ @@ -42,11 +41,6 @@ public final class RegistryTestServerMain { private static final String LIGHT_PURPLE = "\u001b[38;5;139m"; private static final String ORANGE = "\u001b[1;38;5;172m"; - @Parameter( - names = "--mode", - description = "UI console debug mode. RAW allows live editing; DEBUG allows rename testing.") - private ConsoleDebug mode = ConsoleDebug.PRODUCTION; - @Parameter( names = "--address", description = "Listening address.", @@ -67,14 +61,10 @@ public final class RegistryTestServerMain { arity = 1) private boolean loginIsAdmin = true; - @Parameter( - names = "--jetty_debug", - description = "Enables Jetty debug logging.") + @Parameter(names = "--jetty_debug", description = "Enables Jetty debug logging.") private boolean jettyDebug; - @Parameter( - names = "--jetty_verbose", - description = "Enables Jetty verbose logging.") + @Parameter(names = "--jetty_verbose", description = "Enables Jetty verbose logging.") private boolean jettyVerbose; @Parameter( @@ -96,7 +86,6 @@ public final class RegistryTestServerMain { } private void run() throws Throwable { - ConsoleDebug.set(mode); if (jettyDebug) { System.setProperty("DEBUG", "true"); } @@ -105,7 +94,7 @@ public final class RegistryTestServerMain { } System.out.printf( - """ +""" CHARLESTON ROAD REGISTRY SHARED REGISTRATION SYSTEM ICANN-GTLD-AGB-20120604 diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java deleted file mode 100644 index de46c893b..000000000 --- a/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.common.testing.NullPointerTester; -import org.junit.jupiter.api.Test; - -/** Unit tests for {@link FormFieldException}. */ -class FormFieldExceptionTest { - - @Test - void testGetFieldName_multiplePropagations_joinsUsingJsonNotation() { - assertThat( - new FormFieldException("This field is required.") - .propagate("attack") - .propagate("cat") - .propagate(0) - .propagate("lol") - .getFieldName()) - .isEqualTo("lol[0].cat.attack"); - } - - @Test - void testGetFieldName_singlePropagations_noFancyJoining() { - assertThat( - new FormFieldException("This field is required.") - .propagate("cat") - .getFieldName()) - .isEqualTo("cat"); - } - - @Test - void testGetFieldName_noPropagations_throwsIse() { - assertThrows( - IllegalStateException.class, - () -> new FormFieldException("This field is required.").getFieldName()); - } - - @Test - void testNullness() { - NullPointerTester tester = new NullPointerTester() - .setDefault(FormField.class, FormField.named("love").build()); - tester.testAllPublicConstructors(FormFieldException.class); - tester.testAllPublicStaticMethods(FormFieldException.class); - tester.testAllPublicInstanceMethods(new FormFieldException("lol")); - } -} diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldTest.java deleted file mode 100644 index 700ac3475..000000000 --- a/core/src/test/java/google/registry/ui/forms/FormFieldTest.java +++ /dev/null @@ -1,486 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.collect.Range.atLeast; -import static com.google.common.collect.Range.atMost; -import static com.google.common.collect.Range.closed; -import static com.google.common.truth.Truth.assertThat; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.testing.NullPointerTester; -import com.google.re2j.Pattern; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import org.junit.jupiter.api.Test; - -/** Unit tests for {@link FormField}. */ -class FormFieldTest { - - private enum ICanHazEnum { - LOL, - CAT - } - - @Test - void testConvert_nullString_notPresent() { - assertThat(FormField.named("lol").build().convert(null)).isEmpty(); - } - - @Test - void testConvert_emptyString_returnsEmpty() { - assertThat(FormField.named("lol").build().convert("")).hasValue(""); - } - - @Test - void testWithDefault_hasValue_returnsValue() { - assertThat(FormField.named("lol").withDefault("default").build().convert("return me!")) - .hasValue("return me!"); - } - - @Test - void testWithDefault_nullValue_returnsDefault() { - assertThat(FormField.named("lol").withDefault("default").build().convert(null)) - .hasValue("default"); - } - - @Test - void testEmptyToNull_emptyString_notPresent() { - assertThat(FormField.named("lol").emptyToNull().build().convert("")).isEmpty(); - } - - @Test - void testEmptyToNullRequired_emptyString_throwsFfe() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> FormField.named("lol").emptyToNull().required().build().convert("")); - assertThat(thrown, equalTo(new FormFieldException("This field is required.").propagate("lol"))); - } - - @Test - void testEmptyToNull_typeMismatch() { - assertThrows( - IllegalStateException.class, () -> FormField.named("lol", Object.class).emptyToNull()); - } - - @Test - void testNamedLong() { - assertThat(FormField.named("lol", Long.class).build().convert(666L)).hasValue(666L); - } - - @Test - void testUppercased() { - FormField field = FormField.named("lol").uppercased().build(); - assertThat(field.convert(null)).isEmpty(); - assertThat(field.convert("foo")).hasValue("FOO"); - assertThat(field.convert("BAR")).hasValue("BAR"); - } - - @Test - void testLowercased() { - FormField field = FormField.named("lol").lowercased().build(); - assertThat(field.convert(null)).isEmpty(); - assertThat(field.convert("foo")).hasValue("foo"); - assertThat(field.convert("BAR")).hasValue("bar"); - } - - @Test - void testIn_passesThroughNull() { - FormField field = - FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build(); - assertThat(field.convert(null)).isEmpty(); - } - - @Test - void testIn_valueIsContainedInSet() { - FormField field = - FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build(); - assertThat(field.convert("foo")).hasValue("foo"); - assertThat(field.convert("bar")).hasValue("bar"); - } - - @Test - void testIn_valueMissingFromSet() { - FormField field = - FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build(); - FormFieldException thrown = assertThrows(FormFieldException.class, () -> field.convert("omfg")); - assertThat(thrown, equalTo(new FormFieldException("Unrecognized value.").propagate("lol"))); - } - - @Test - void testRange_hasLowerBound_nullValue_passesThrough() { - assertThat(FormField.named("lol").range(atLeast(5)).build().convert(null)).isEmpty(); - } - - @Test - void testRange_minimum_stringLengthEqualToMinimum_doesNothing() { - assertThat(FormField.named("lol").range(atLeast(5)).build().convert("hello")).hasValue("hello"); - } - - @Test - void testRange_minimum_stringLengthShorterThanMinimum_throwsFfe() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> FormField.named("lol").range(atLeast(4)).build().convert("lol")); - assertThat(thrown).hasMessageThat().contains("Number of characters (3) not in range [4"); - } - - @Test - void testRange_noLowerBound_nullValue_passThrough() { - assertThat(FormField.named("lol").range(atMost(5)).build().convert(null)).isEmpty(); - } - - @Test - void testRange_maximum_stringLengthEqualToMaximum_doesNothing() { - assertThat(FormField.named("lol").range(atMost(5)).build().convert("hello")).hasValue("hello"); - } - - @Test - void testRange_maximum_stringLengthShorterThanMaximum_throwsFfe() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> FormField.named("lol").range(atMost(5)).build().convert("omgomg")); - assertThat(thrown).hasMessageThat().contains("Number of characters (6) not in range"); - } - - @Test - void testRange_numericTypes() { - FormField.named("lol", Byte.class).range(closed(5, 10)).build().convert((byte) 7); - FormField.named("lol", Short.class).range(closed(5, 10)).build().convert((short) 7); - FormField.named("lol", Integer.class).range(closed(5, 10)).build().convert(7); - FormField.named("lol", Long.class).range(closed(5, 10)).build().convert(7L); - FormField.named("lol", Float.class).range(closed(5, 10)).build().convert(7F); - FormField.named("lol", Double.class).range(closed(5, 10)).build().convert(7D); - } - - @Test - void testRange_typeMismatch() { - assertThrows( - IllegalStateException.class, () -> FormField.named("lol", Object.class).range(atMost(5))); - } - - @Test - void testMatches_matches_doesNothing() { - assertThat(FormField.named("lol").matches(Pattern.compile("[a-z]+")).build().convert("abc")) - .hasValue("abc"); - } - - @Test - void testMatches_mismatch_throwsFfeAndShowsDefaultErrorMessageWithPattern() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> - FormField.named("lol") - .matches(Pattern.compile("[a-z]+")) - .build() - .convert("123abc456")); - assertThat( - thrown, equalTo(new FormFieldException("Must match pattern: [a-z]+").propagate("lol"))); - } - - @Test - void testMatches_typeMismatch() { - assertThrows( - IllegalStateException.class, - () -> FormField.named("lol", Object.class).matches(Pattern.compile("."))); - } - - @Test - void testRetains() { - assertThat( - FormField.named("lol") - .retains(CharMatcher.anyOf("0123456789")) - .build() - .convert(" 123 1593-43 453 45 4 4 \t")) - .hasValue("1231593434534544"); - } - - @Test - void testCast() { - assertThat( - FormField.named("lol") - .transform(Integer.class, Integer::parseInt) - .build() - .convert("123")) - .hasValue(123); - } - - @Test - void testCast_twice() { - assertThat( - FormField.named("lol") - .transform(Object.class, Integer::parseInt) - .transform(String.class, Object::toString) - .build() - .convert("123")) - .hasValue("123"); - } - - @Test - void testAsList_null_notPresent() { - assertThat(FormField.named("lol").asList().build().convert(null)).isEmpty(); - } - - @Test - void testAsList_empty_returnsEmpty() { - assertThat(FormField.named("lol").asList().build().convert(ImmutableList.of())) - .hasValue(ImmutableList.of()); - } - - @Test - void testAsListEmptyToNullRequired_empty_throwsFfe() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> - FormField.named("lol") - .asList() - .emptyToNull() - .required() - .build() - .convert(ImmutableList.of())); - assertThat(thrown, equalTo(new FormFieldException("This field is required.").propagate("lol"))); - } - - @Test - void testListEmptyToNull_empty_notPresent() { - assertThat(FormField.named("lol").asList().emptyToNull().build().convert(ImmutableList.of())) - .isEmpty(); - } - - @Test - void testAsEnum() { - FormField omgField = - FormField.named("omg").asEnum(ICanHazEnum.class).build(); - assertThat(omgField.convert("LOL")).hasValue(ICanHazEnum.LOL); - assertThat(omgField.convert("CAT")).hasValue(ICanHazEnum.CAT); - } - - @Test - void testAsEnum_lowercase_works() { - FormField omgField = - FormField.named("omg").asEnum(ICanHazEnum.class).build(); - assertThat(omgField.convert("lol")).hasValue(ICanHazEnum.LOL); - assertThat(omgField.convert("cat")).hasValue(ICanHazEnum.CAT); - } - - @Test - void testAsEnum_badInput_throwsFfe() { - FormField omgField = - FormField.named("omg").asEnum(ICanHazEnum.class).build(); - FormFieldException thrown = - assertThrows(FormFieldException.class, () -> omgField.convert("helo")); - assertThat( - thrown, - equalTo( - new FormFieldException("Enum ICanHazEnum does not contain 'helo'").propagate("omg"))); - } - - @Test - void testSplitList() { - FormField> field = - FormField.named("lol").asList(Splitter.on(',').omitEmptyStrings()).build(); - assertThat(field.convert("oh,my,goth").get()).containsExactly("oh", "my", "goth").inOrder(); - assertThat(field.convert("").get()).isEmpty(); - assertThat(field.convert(null)).isEmpty(); - } - - @Test - void testSplitSet() { - FormField> field = - FormField.named("lol").uppercased().asSet(Splitter.on(',').omitEmptyStrings()).build(); - assertThat(field.convert("oh,my,goth").get()).containsExactly("OH", "MY", "GOTH").inOrder(); - assertThat(field.convert("").get()).isEmpty(); - assertThat(field.convert(null)).isEmpty(); - } - - @Test - void testAsList() { - assertThat( - FormField.named("lol") - .asList() - .build() - .convert(ImmutableList.of("lol", "cat", "")) - .get()) - .containsExactly("lol", "cat", "") - .inOrder(); - } - - @Test - void testAsList_trimmedEmptyToNullOnItems() { - assertThat( - FormField.named("lol") - .trimmed() - .emptyToNull() - .matches(Pattern.compile("[a-z]+")) - .asList() - .range(closed(1, 2)) - .build() - .convert(ImmutableList.of("lol\n", "\tcat ")) - .get()) - .containsExactly("lol", "cat") - .inOrder(); - } - - @Test - void testAsList_nullElements_getIgnored() { - assertThat( - FormField.named("lol") - .emptyToNull() - .asList() - .build() - .convert(ImmutableList.of("omg", "")) - .get()) - .containsExactly("omg"); - } - - @Test - void testAsListRequiredElements_nullElement_throwsFfeWithIndex() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> - FormField.named("lol") - .emptyToNull() - .required() - .asList() - .build() - .convert(ImmutableList.of("omg", ""))); - assertThat( - thrown, - equalTo(new FormFieldException("This field is required.").propagate(1).propagate("lol"))); - } - - @Test - void testMapAsListRequiredElements_nullElement_throwsFfeWithIndexAndKey() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> - FormField.mapNamed("lol") - .transform( - String.class, - input -> - FormField.named("cat") - .emptyToNull() - .required() - .build() - .extractUntyped(input) - .get()) - .asList() - .build() - .convert(ImmutableList.of(ImmutableMap.of("cat", "")))); - assertThat( - thrown, - equalTo( - new FormFieldException("This field is required.") - .propagate("cat") - .propagate(0) - .propagate("lol"))); - } - - @Test - void testAsListTrimmed_typeMismatch() { - FormField.named("lol").trimmed().asList(); - assertThrows(IllegalStateException.class, () -> FormField.named("lol").asList().trimmed()); - } - - @Test - void testAsMatrix() { - assertThat( - FormField.named("lol", Integer.class) - .transform(input -> input * 2) - .asList() - .asList() - .build() - .convert( - Lists.cartesianProduct( - ImmutableList.of(ImmutableList.of(1, 2), ImmutableList.of(3, 4)))) - .get()) - .containsExactly( - ImmutableList.of(2, 6), - ImmutableList.of(2, 8), - ImmutableList.of(4, 6), - ImmutableList.of(4, 8)) - .inOrder(); - } - - @Test - void testAsSet() { - assertThat( - FormField.named("lol") - .asSet() - .build() - .convert(ImmutableList.of("lol", "cat", "cat")) - .get()) - .containsExactly("lol", "cat"); - } - - @Test - void testTrimmed() { - assertThat(FormField.named("lol").trimmed().build().convert(" \thello \t\n")).hasValue("hello"); - } - - @Test - void testTrimmed_typeMismatch() { - assertThrows(IllegalStateException.class, () -> FormField.named("lol", Object.class).trimmed()); - } - - @Test - void testAsBuilder() { - FormField field = FormField.named("omg").uppercased().build(); - assertThat(field.name()).isEqualTo("omg"); - assertThat(field.convert("hello")).hasValue("HELLO"); - field = field.asBuilder().build(); - assertThat(field.name()).isEqualTo("omg"); - assertThat(field.convert("hello")).hasValue("HELLO"); - } - - @Test - void testAsBuilderNamed() { - FormField field = FormField.named("omg").uppercased().build(); - assertThat(field.name()).isEqualTo("omg"); - assertThat(field.convert("hello")).hasValue("HELLO"); - field = field.asBuilderNamed("bog").build(); - assertThat(field.name()).isEqualTo("bog"); - assertThat(field.convert("hello")).hasValue("HELLO"); - } - - @Test - void testNullness() { - NullPointerTester tester = - new NullPointerTester() - .setDefault(Class.class, Object.class) - .setDefault(Function.class, x -> x) - .setDefault(Pattern.class, Pattern.compile(".")) - .setDefault(String.class, "hello.com"); - tester.testAllPublicStaticMethods(FormField.class); - tester.testAllPublicInstanceMethods(FormField.named("lol")); - tester.testAllPublicInstanceMethods(FormField.named("lol").build()); - } -} diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java deleted file mode 100644 index 6fceb1e21..000000000 --- a/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// 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 -// -// http://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 google.registry.ui.forms; - -import static com.google.common.truth.Truth.assertThat; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.common.testing.NullPointerTester; -import org.junit.jupiter.api.Test; - -/** Unit tests for {@link FormFields}. */ -class FormFieldsTest { - - @Test - void testXsToken_collapsesAndTrimsWhitespace() { - assertThat(FormFields.XS_TOKEN.convert(" hello \r\n\t there\n")).hasValue("hello there"); - } - - @Test - void testXsNormalizedString_extraSpaces_doesntCare() { - assertThat(FormFields.XS_NORMALIZED_STRING.convert("hello there")).hasValue("hello there"); - } - - @Test - void testXsNormalizedString_sideSpaces_doesntCare() { - assertThat(FormFields.XS_NORMALIZED_STRING.convert(" hello there ")).hasValue(" hello there "); - } - - @Test - void testXsNormalizedString_containsNonSpaceWhitespace_fails() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, - () -> FormFields.XS_NORMALIZED_STRING.convert(" hello \r\n\t there\n")); - assertThat( - thrown, - equalTo( - new FormFieldException("Must not contain tabs or multiple lines.") - .propagate("xsNormalizedString"))); - } - - @Test - void testXsEppE164PhoneNumber_nanpaNumber_validates() { - assertThat(FormFields.XS_NORMALIZED_STRING.convert("+1.2125650000")).hasValue("+1.2125650000"); - } - - @Test - void testXsEppE164PhoneNumber_londonNumber_validates() { - assertThat(FormFields.XS_NORMALIZED_STRING.convert("+44.2011112222")) - .hasValue("+44.2011112222"); - } - - @Test - void testXsEppE164PhoneNumber_localizedNumber_fails() { - FormFieldException thrown = - assertThrows( - FormFieldException.class, () -> FormFields.PHONE_NUMBER.convert("(212) 565-0000")); - assertThat(thrown) - .hasMessageThat() - .contains("Must be a valid +E.164 phone number, e.g. +1.2125650000"); - } - - @Test - void testXsEppE164PhoneNumber_appliesXsTokenTransform() { - assertThat(FormFields.PHONE_NUMBER.convert(" +1.2125650000 \r")).hasValue("+1.2125650000"); - } - - @Test - void testXsEppRoid_correctSyntax_validates() { - assertThat(FormFields.ROID.convert("SH8013-REP")).hasValue("SH8013-REP"); - } - - @Test - void testXsEppRoid_lowerCase_validates() { - assertThat(FormFields.ROID.convert("sh8013-rep")).hasValue("sh8013-rep"); - } - - @Test - void testXsEppRoid_missingHyphen_fails() { - FormFieldException thrown = - assertThrows(FormFieldException.class, () -> FormFields.ROID.convert("SH8013REP")); - assertThat(thrown).hasMessageThat().contains("Please enter a valid EPP ROID, e.g. SH8013-REP"); - } - - @Test - void testXsEppRoid_appliesXsTokenTransform() { - assertThat(FormFields.ROID.convert("\n FOO-BAR \r")).hasValue("FOO-BAR"); - } - - @Test - void testNullness() { - new NullPointerTester().testAllPublicStaticMethods(FormFields.class); - } -}