diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/ConnectorArguments.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/ConnectorArguments.java index 452868c87..a981845ca 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/ConnectorArguments.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/ConnectorArguments.java @@ -28,10 +28,8 @@ import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Predicates; import com.google.common.base.Strings; -import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; import com.google.edwmigration.dumper.application.dumper.ZonedParser.DayOffset; -import com.google.edwmigration.dumper.application.dumper.annotations.RespectsInput; import com.google.edwmigration.dumper.application.dumper.connector.Connector; import com.google.edwmigration.dumper.application.dumper.connector.ConnectorProperty; import com.google.edwmigration.dumper.application.dumper.connector.ConnectorPropertyWithDefault; @@ -43,17 +41,11 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.CheckForNull; @@ -63,7 +55,6 @@ import joptsimple.OptionSpec; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.core.annotation.AnnotationUtils; /** @author shevek */ public class ConnectorArguments extends DefaultArguments { @@ -311,7 +302,6 @@ public class ConnectorArguments extends DefaultArguments { private final OptionSpec optionOutputContinue = parser.accepts("continue", "Continues writing a previous output file."); - // TODO: Make this be an ISO instant. @Deprecated private final OptionSpec optionQueryLogEarliestTimestamp = parser @@ -606,100 +596,16 @@ public ConnectorArguments(@Nonnull String... args) throws IOException { super(args); } - private static class InputDescriptor implements Comparable { - - public enum Category { - Arg, - Env, - Other - } - - private final RespectsInput annotation; - - public InputDescriptor(RespectsInput annotation) { - this.annotation = annotation; - } - - @Nonnull - public Category getCategory() { - if (!Strings.isNullOrEmpty(annotation.arg())) { - return Category.Arg; - } - if (!Strings.isNullOrEmpty(annotation.env())) { - return Category.Env; - } - return Category.Other; - } - - @Nonnull - public String getKey() { - switch (getCategory()) { - case Arg: - return "--" + annotation.arg(); - case Env: - return annotation.env(); - default: - return String.valueOf(annotation.hashCode()); - } - } - - @Override - public int compareTo(InputDescriptor o) { - return ComparisonChain.start() - .compare(getCategory(), o.getCategory()) - .compare(annotation.order(), o.annotation.order()) - .result(); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - String key = getKey(); - buf.append(key).append(StringUtils.repeat(' ', 12 - key.length())); - if (getCategory() == Category.Env) { - buf.append(" (environment variable)"); - } - String defaultValue = annotation.defaultValue(); - if (!Strings.isNullOrEmpty(defaultValue)) { - buf.append(" (default: ").append(defaultValue).append(")"); - } - buf.append(" ").append(annotation.description()); - String required = annotation.required(); - if (!Strings.isNullOrEmpty(required)) { - buf.append(" (Required ").append(required).append(".)"); - } - return buf.toString(); - } - } - - @Nonnull - private static Collection getAcceptsInputs(@Nonnull Connector connector) { - Map tmp = new HashMap<>(); - Class connectorType = connector.getClass(); - while (connectorType != null) { - Set respectsInputs = - AnnotationUtils.getDeclaredRepeatableAnnotations(connectorType, RespectsInput.class); - for (RespectsInput respectsInput : respectsInputs) { - InputDescriptor descriptor = new InputDescriptor(respectsInput); - tmp.putIfAbsent(descriptor.getKey(), descriptor); - } - connectorType = connectorType.getSuperclass(); - } - - List out = new ArrayList<>(tmp.values()); - Collections.sort(out); - return out; - } - @Override - protected void printHelpOn(PrintStream out, OptionSet o) throws IOException { + protected void printHelpOn(@Nonnull PrintStream out, OptionSet o) throws IOException { out.append(HELP_INFO); super.printHelpOn(out, o); + ConnectorRepository repository = ConnectorRepository.getInstance(); // if --connector provided, print only that if (o.has(connectorNameOption)) { String helpOnConnector = o.valueOf(connectorNameOption); - Connector connector = ConnectorRepository.getInstance().getByName(helpOnConnector); + Connector connector = repository.getByName(helpOnConnector); if (connector != null) { out.append("\nSelected connector:\n"); printConnectorHelp(out, connector); @@ -707,22 +613,14 @@ protected void printHelpOn(PrintStream out, OptionSet o) throws IOException { } } out.append("\nAvailable connectors:\n"); - for (Connector connector : ConnectorRepository.getInstance().getAllConnectors()) { + for (Connector connector : repository.getAllConnectors()) { printConnectorHelp(out, connector); } } - private void printConnectorHelp(@Nonnull Appendable out, @Nonnull Connector connector) + private static void printConnectorHelp(@Nonnull Appendable out, @Nonnull Connector connector) throws IOException { - out.append("* " + connector.getName()); - String description = connector.getDescription(); - if (!description.isEmpty()) { - out.append(" - " + description); - } - out.append("\n"); - for (InputDescriptor descriptor : getAcceptsInputs(connector)) { - out.append(String.format("%8s%s\n", "", descriptor)); - } + connector.printHelp(out); ConnectorProperties.printHelp(out, connector); } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/InputDescriptor.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/InputDescriptor.java new file mode 100644 index 000000000..c7f41b0c8 --- /dev/null +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/InputDescriptor.java @@ -0,0 +1,121 @@ +/* + * Copyright 2022-2025 Google LLC + * Copyright 2013-2021 CompilerWorks + * + * 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 com.google.edwmigration.dumper.application.dumper; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.joining; + +import com.google.edwmigration.dumper.application.dumper.annotations.RespectsInput; +import java.util.ArrayList; +import java.util.Comparator; +import javax.annotation.Nonnull; + +public final class InputDescriptor { + private enum Category { + ARGUMENT(1) { + @Override + String getKey(@Nonnull RespectsInput annotation) { + return "--" + annotation.arg(); + } + }, + ENVIRONMENT(2) { + @Override + String getKey(@Nonnull RespectsInput annotation) { + return annotation.env(); + } + }, + OTHER(3) { + @Override + String getKey(@Nonnull RespectsInput annotation) { + return String.valueOf(annotation.hashCode()); + } + }; + + final int order; + + abstract String getKey(@Nonnull RespectsInput annotation); + + Category(int order) { + this.order = order; + } + } + + private final RespectsInput annotation; + + public InputDescriptor(RespectsInput annotation) { + this.annotation = annotation; + } + + @Nonnull + private Category getCategory() { + if (!isNullOrEmpty(annotation.arg())) { + return Category.ARGUMENT; + } + if (!isNullOrEmpty(annotation.env())) { + return Category.ENVIRONMENT; + } + return Category.OTHER; + } + + public static Comparator comparator() { + return comparing(InputDescriptor::categoryOrder) + .thenComparing(InputDescriptor::annotationOrder); + } + + @Override + public String toString() { + ArrayList buf = new ArrayList<>(); + buf.add(String.format("%-12s", getKey())); + if (getCategory() == Category.ENVIRONMENT) { + buf.add("(environment variable)"); + } + buf.add(defaultHint(annotation.defaultValue())); + buf.add(annotation.description()); + buf.add(requiredHint(annotation.required())); + + return buf.stream().filter(el -> !el.isEmpty()).collect(joining(" ")); + } + + public String getKey() { + return getCategory().getKey(annotation); + } + + private String defaultHint(String value) { + if (isNullOrEmpty(value)) { + return ""; + } else { + return String.format("(default: %s)", value); + } + } + + private String requiredHint(String value) { + if (isNullOrEmpty(value)) { + return ""; + } else { + return String.format("(Required %s.)", value); + } + } + + private int annotationOrder() { + return annotation.order(); + } + + private int categoryOrder() { + return getCategory().order; + } +} diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/Connector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/Connector.java index c7e895914..2041beb2e 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/Connector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/Connector.java @@ -18,13 +18,23 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static org.springframework.core.annotation.AnnotationUtils.getDeclaredRepeatableAnnotations; +import autovalue.shaded.com.google.common.collect.ImmutableList; import com.google.edwmigration.dumper.application.dumper.ConnectorArguments; +import com.google.edwmigration.dumper.application.dumper.InputDescriptor; +import com.google.edwmigration.dumper.application.dumper.annotations.RespectsInput; import com.google.edwmigration.dumper.application.dumper.handle.Handle; import com.google.edwmigration.dumper.application.dumper.task.Task; +import java.io.IOException; import java.time.Clock; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; /** @author shevek */ @@ -77,4 +87,39 @@ void addTasksTo(@Nonnull List> out, @Nonnull ConnectorArguments @Nonnull Iterable getPropertyConstants(); + + default void printHelp(@Nonnull Appendable out) throws IOException { + out.append("* " + getName()); + String description = getDescription(); + if (!description.isEmpty()) { + out.append(" - " + description); + } + out.append("\n"); + for (InputDescriptor descriptor : getAcceptsInputs(this)) { + out.append(String.format("%8s%s\n", "", descriptor)); + } + } + + @Nonnull + static Collection getAcceptsInputs(@Nonnull Connector connector) { + + ArrayList> classes = new ArrayList<>(); + for (Class type = connector.getClass(); type != null; type = type.getSuperclass()) { + classes.add(type); + } + + Map map = new HashMap<>(); + for (Class type : classes) { + Set respectsInputs = + getDeclaredRepeatableAnnotations(type, RespectsInput.class); + for (RespectsInput item : respectsInputs) { + InputDescriptor descriptor = new InputDescriptor(item); + map.putIfAbsent(descriptor.getKey(), descriptor); + } + } + + return map.values().stream() + .sorted(InputDescriptor.comparator()) + .collect(ImmutableList.toImmutableList()); + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnector.java index 2c79ccefa..a4236692f 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnector.java @@ -31,6 +31,7 @@ import com.google.edwmigration.dumper.application.dumper.annotations.RespectsInput; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsInputs; import com.google.edwmigration.dumper.application.dumper.connector.AbstractJdbcConnector; +import com.google.edwmigration.dumper.application.dumper.connector.Connector; import com.google.edwmigration.dumper.application.dumper.handle.Handle; import com.google.edwmigration.dumper.application.dumper.handle.JdbcHandle; import com.google.edwmigration.dumper.application.dumper.task.AbstractJdbcTask; @@ -252,4 +253,10 @@ String sanitizeDatabaseName(@Nonnull String databaseName) throws MetadataDumperU } return trimmedName; } + + static String describeAsDelegate(Connector connector, String baseName) { + String summary = String.format("* %s - %s\n", connector.getName(), connector.getDescription()); + String details = String.format("%8s[same options as '%s']\n", "", baseName); + return summary + details; + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageLogsConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageLogsConnector.java index f5ce383c2..20e401aa5 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageLogsConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageLogsConnector.java @@ -20,11 +20,12 @@ import com.google.auto.service.AutoService; import com.google.edwmigration.dumper.application.dumper.connector.Connector; +import java.io.IOException; import javax.annotation.Nonnull; /** @author shevek */ @AutoService(Connector.class) -public class SnowflakeAccountUsageLogsConnector extends SnowflakeLogsConnector { +public final class SnowflakeAccountUsageLogsConnector extends SnowflakeLogsConnector { public SnowflakeAccountUsageLogsConnector() { super("snowflake-account-usage-logs", USAGE_ONLY_SOURCE); @@ -35,4 +36,9 @@ public SnowflakeAccountUsageLogsConnector() { public String getDescription() { return "Dumps logs from Snowflake, using ACCOUNT_USAGE only."; } + + @Override + public void printHelp(@Nonnull Appendable out) throws IOException { + out.append(AbstractSnowflakeConnector.describeAsDelegate(this, "snowflake-logs")); + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageMetadataConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageMetadataConnector.java index ac2a2a5df..cf4417b26 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageMetadataConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeAccountUsageMetadataConnector.java @@ -20,6 +20,7 @@ import com.google.auto.service.AutoService; import com.google.edwmigration.dumper.application.dumper.connector.Connector; +import java.io.IOException; import javax.annotation.Nonnull; /** @author shevek */ @@ -35,4 +36,9 @@ public SnowflakeAccountUsageMetadataConnector() { public String getDescription() { return "Dumps metadata from Snowflake, using ACCOUNT_USAGE only."; } + + @Override + public void printHelp(@Nonnull Appendable out) throws IOException { + out.append(AbstractSnowflakeConnector.describeAsDelegate(this, "snowflake")); + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaLogsConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaLogsConnector.java index 4c26edb7f..bc55cbf21 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaLogsConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaLogsConnector.java @@ -20,6 +20,7 @@ import com.google.auto.service.AutoService; import com.google.edwmigration.dumper.application.dumper.connector.Connector; +import java.io.IOException; import javax.annotation.Nonnull; /** @@ -39,4 +40,9 @@ public SnowflakeInformationSchemaLogsConnector() { public String getDescription() { return "Dumps logs from Snowflake, using INFORMATION_SCHEMA only."; } + + @Override + public void printHelp(@Nonnull Appendable out) throws IOException { + out.append(AbstractSnowflakeConnector.describeAsDelegate(this, "snowflake-logs")); + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaMetadataConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaMetadataConnector.java index 3e5ca8995..b8453b457 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaMetadataConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeInformationSchemaMetadataConnector.java @@ -20,6 +20,7 @@ import com.google.auto.service.AutoService; import com.google.edwmigration.dumper.application.dumper.connector.Connector; +import java.io.IOException; import javax.annotation.Nonnull; /** @author shevek */ @@ -35,4 +36,9 @@ public SnowflakeInformationSchemaMetadataConnector() { public String getDescription() { return "Dumps metadata from Snowflake, using INFORMATION_SCHEMA only."; } + + @Override + public void printHelp(@Nonnull Appendable out) throws IOException { + out.append(AbstractSnowflakeConnector.describeAsDelegate(this, "snowflake")); + } } diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLiteConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLiteConnector.java index 2ada797b5..016198e19 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLiteConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLiteConnector.java @@ -19,6 +19,7 @@ import com.google.auto.service.AutoService; import com.google.edwmigration.dumper.application.dumper.ConnectorArguments; import com.google.edwmigration.dumper.application.dumper.MetadataDumperUsageException; +import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentAssessment; import com.google.edwmigration.dumper.application.dumper.connector.Connector; import com.google.edwmigration.dumper.application.dumper.task.DumpMetadataTask; import com.google.edwmigration.dumper.application.dumper.task.FormatTask; @@ -32,6 +33,7 @@ @AutoService(Connector.class) @ParametersAreNonnullByDefault +@RespectsArgumentAssessment public final class SnowflakeLiteConnector extends AbstractSnowflakeConnector { private static final String FORMAT_NAME = "snowflake-lite.zip"; diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLogsConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLogsConnector.java index e3145eaf6..e77d974ff 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLogsConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeLogsConnector.java @@ -23,6 +23,7 @@ import com.google.common.base.CaseFormat; import com.google.edwmigration.dumper.application.dumper.ConnectorArguments; import com.google.edwmigration.dumper.application.dumper.MetadataDumperUsageException; +import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentAssessment; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentDatabaseForConnection; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentQueryLogDays; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentQueryLogEnd; @@ -55,6 +56,7 @@ /** @author shevek */ @AutoService(Connector.class) +@RespectsArgumentAssessment @RespectsArgumentDatabaseForConnection @RespectsArgumentQueryLogDays @RespectsArgumentQueryLogStart diff --git a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeMetadataConnector.java b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeMetadataConnector.java index 19e079449..b404ba00f 100644 --- a/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeMetadataConnector.java +++ b/dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/SnowflakeMetadataConnector.java @@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.edwmigration.dumper.application.dumper.ConnectorArguments; +import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentAssessment; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentDatabaseForConnection; import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentDatabasePredicate; import com.google.edwmigration.dumper.application.dumper.connector.Connector; @@ -51,6 +52,7 @@ * @author matt */ @AutoService(Connector.class) +@RespectsArgumentAssessment @RespectsArgumentDatabaseForConnection @RespectsArgumentDatabasePredicate public class SnowflakeMetadataConnector extends AbstractSnowflakeConnector diff --git a/dumper/app/src/test/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnectorTest.java b/dumper/app/src/test/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnectorTest.java index d6e223e2f..2dffb1f75 100644 --- a/dumper/app/src/test/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnectorTest.java +++ b/dumper/app/src/test/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnectorTest.java @@ -28,10 +28,11 @@ import java.util.stream.IntStream; import org.junit.Assert; import org.junit.Test; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -@RunWith(JUnit4.class) +@RunWith(Theories.class) public class AbstractSnowflakeConnectorTest extends AbstractConnectorTest { private static final ImmutableList ARGS = ImmutableList.of( @@ -43,6 +44,33 @@ public class AbstractSnowflakeConnectorTest extends AbstractConnectorTest { private final SnowflakeMetadataConnector metadataConnector = new SnowflakeMetadataConnector(); + public enum TestCase { + LOGS(SnowflakeLogsConnector.class), + LOGS_AU(SnowflakeAccountUsageLogsConnector.class), + LOGS_IS(SnowflakeInformationSchemaLogsConnector.class), + META(SnowflakeMetadataConnector.class), + META_AU(SnowflakeAccountUsageMetadataConnector.class), + META_IS(SnowflakeInformationSchemaMetadataConnector.class); + + final Class subclass; + + TestCase(Class subclass) { + this.subclass = subclass; + } + } + + @Theory + public void describeAsDelegate_success(TestCase testCase) throws Exception { + AbstractSnowflakeConnector connector = testCase.subclass.newInstance(); + + String description = AbstractSnowflakeConnector.describeAsDelegate(connector, "test-connector"); + + assertTrue(description, description.contains(connector.getName())); + assertTrue(description, description.contains(connector.getDescription())); + assertTrue(description, description.contains("[same options as 'test-connector']")); + assertTrue(description, description.endsWith("\n")); + } + @Test public void openConnection_failsForVeryLongInput() throws IOException { // 262 characters