From 29bfa0469b07a73910af5244299d5547619731d2 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 12 Sep 2023 15:21:47 +0200 Subject: [PATCH] #9 Add design for integration test framework (#138) Co-authored-by: Christoph Kuhnke --- .github/workflows/ci-build.yml | 2 +- .github/workflows/render-design.yml | 1 + doc/changes/changes_0.5.2.md | 6 +- doc/design.md | 75 +++++++++++++--- doc/developer_guide.md | 33 ++----- doc/extension_developer_guide.md | 85 +++++++++++++++++++ doc/system_requirements.md | 16 ++-- .../itest/ExtensionManagerClient.java | 1 + .../itest/ExtensionManagerProcess.java | 1 + .../itest/PreviousExtensionVersion.java | 1 + .../exasol/extensionmanager/ExampleIT.java | 54 ++++++++++++ .../itest/ExasolMetadataIT.java | 5 +- .../itest/ExtensionManagerClientIT.java | 9 +- .../itest/ExtensionManagerClientTest.java | 2 +- .../itest/ExtensionManagerSetupIT.java | 5 +- .../itest/IntegrationTestCommon.java | 2 + .../itest/PreviousExtensionVersionTest.java | 1 + pkg/extensionAPI/extensionApi.go | 2 + pkg/extensionAPI/extensionApi_test.go | 1 + .../testExtensionBuilder.go | 6 +- 20 files changed, 248 insertions(+), 60 deletions(-) create mode 100644 doc/extension_developer_guide.md create mode 100644 extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/ExampleIT.java diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 7a2bae9a..653b59a8 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -86,7 +86,7 @@ jobs: - name: Integration tests run: | - # -p 1 tells go not to run the tests in parallel. See the developers_guide. + # -p 1 tells go not to run the tests in parallel. See the developer guide. go test -v -p 1 -count 1 -coverprofile=coverage.out ./... - name: Prepare testing extension diff --git a/.github/workflows/render-design.yml b/.github/workflows/render-design.yml index a6866e5b..bcfc8a56 100644 --- a/.github/workflows/render-design.yml +++ b/.github/workflows/render-design.yml @@ -23,6 +23,7 @@ jobs: mkdir gh-pages cd gh-pages pandoc ../doc/design.md -o design.html --filter pandoc-plantuml + pandoc ../doc/system_requirements.md -o system_requirements.html --filter pandoc-plantuml - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@v4.3.0 with: diff --git a/doc/changes/changes_0.5.2.md b/doc/changes/changes_0.5.2.md index e9d328d7..0b12594b 100644 --- a/doc/changes/changes_0.5.2.md +++ b/doc/changes/changes_0.5.2.md @@ -4,12 +4,16 @@ Code name: ## Summary -This release updates the upload process for the extension registry to verify that the extension URLs are valid. +This release updates the upload process for the extension registry to verify that the extension URLs are valid. It also adds design, requirements and user guide for the integration testing framework. ## Features * #129: Added verification for extension URLs before uploading to registry +## Documentation + +* #9: Add design, requirements and user guide for integration testing framework + ## Refactoring * #139: Converted `extensionForTesting` to JavaScript to speedup tests diff --git a/doc/design.md b/doc/design.md index 6af7623b..dc04bdb3 100644 --- a/doc/design.md +++ b/doc/design.md @@ -173,9 +173,18 @@ Needs: impl, utest, itest ### Extension Definitions `dsn~extension-definition~1` -Each extension might be implemented in an arbitrary programming language and typically are based on a so-called [user defined function](system_requirements.md#terms-and-abbreviations). In order to allow EM to manage multiple heterogenous extensions in a unique way, each extension is represented by small wrapper implementing a uniform interface. This wrapper is called an "extension definition". +Each extension might be implemented in an arbitrary programming language and typically is based on a so-called [user defined function](system_requirements.md#terms-and-abbreviations). In order to allow EM to manage multiple heterogenous extensions in a unique way, each extension is represented by small wrapper implementing a uniform interface. This wrapper is called an "extension definition". -The interface is defined in [`extension-manager-interface`](https://github.com/exasol/extension-manager-interface/): +Covers: + +* [`req~extension~1`](system_requirements.md#install-required-artifacts) + +Needs: impl, utest, itest + +#### Extension API Interface +`dsn~extension-api~1` + +Each [extension definition](#extension-definitions) implements the TypeScript interface defined in [`extension-manager-interface`](https://github.com/exasol/extension-manager-interface/). This allows EM to uniformly manage all extensions that implement this interface. ```plantuml @startuml @@ -190,9 +199,9 @@ ExasolExtension <-- "mysql-virtual-schema (repo).MySqlExtensionDefinition" Covers: -* [`req~extension~1`](system_requirements.md#install-required-artifacts) +* [`req~extension-api~1`](system_requirements.md#extension-api) -Needs: impl, utest, itest +Needs: impl, utest #### Storage for Extension Definitions `dsn~extension-definitions-storage~1` @@ -323,7 +332,7 @@ Covers: Each parameter definition is attached to a specific version of the extension. -Rationale: Parameters can change over time, see [Updates](#updates). +Rationale: Parameters can change over time, see [Upgrades](#upgrades). Covers: * [`req~define-configuration-parameters~1`](system_requirements.md#parameter-types) @@ -384,6 +393,46 @@ See design decision [against a callback for the client side validation](#callbac Covers: * [`req~validate-parameter-values~1`](system_requirements.md#ui-languages) +### Extension Integration Testing Framework + +The Extension Integration Testing Framework for Java (EITFJ) allows [extension developers](system_requirements.md#extension-developers) to create integration tests for an extension involving its definition](#extension-definitions) and implementation. + +The EITFJ is written in Java as most extensions like virtual schemas are also written in Java. This way it's easy to add integration tests for an extension into the existing Maven build. + +#### Starting Extension Manager during Integration Tests +`dsn~eitfj-start-extension-manager~1` + +The EITFJ provides a method for installing and starting an [Extension Manager REST interface](#em-provides-a-rest-interface). + +Covers: +* [`req~extension-testing-framework~1`](system_requirements.md#integration-test-framework-for-extensions) + +Needs: impl, itest, doc + +#### Accessing the Extension Manager REST Interface +`dsn~eitfj-access-extension-manager-rest-interface~1` + +The EITFJ provides a Java API for accessing the EM REST interface. + +Rationale: +This simplifies integration tests and avoids code duplication. + +Covers: +* [`req~extension-testing-framework~1`](system_requirements.md#integration-test-framework-for-extensions) + +Needs: impl, utest, itest, doc + +#### Preparing Previous Extension Versions +`dsn~eitfj-prepare-previous-extension-version~1` + +The EITFJ provides a Java API for preparing previous versions of an extension. This allows writing integration tests for upgrading from a previous version to the current version of an extension. + +Covers: +* [`req~extension-testing-framework~1`](system_requirements.md#integration-test-framework-for-extensions) +* [`req~upgrade-extension~1`](system_requirements.md#upgrade-extension) + +Needs: impl, utest + ## Runtime ### Listing Extensions @@ -475,7 +524,7 @@ Notes: * See details about [BucketsFS Buckets in Exasol SaaS](#bucketsfs-buckets-in-exasol-saas) in the next section. Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) * [`req~define-configuration-parameters~1`](system_requirements.md#parameter-types) * [`req~uninstall-extension~1`](system_requirements.md#uninstalling-extensions) @@ -567,7 +616,7 @@ Installation "1" o-- "*" Instance ``` Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) #### Installation Metadata @@ -578,7 +627,7 @@ Extensions don't store their own metadata. Instead they read information about e However, for example for reading back the credentials stored in a connection, EM uses a temporary UDF that reads back the secret value. Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest @@ -627,7 +676,7 @@ database Database { ``` Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest @@ -638,7 +687,7 @@ EM can upgrade an installed extensions and its instances to the latest version. Covers: * [`req~upgrade-extension~1`](system_requirements.md#uninstalling-extensions) -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest @@ -658,7 +707,7 @@ Rationale: * While EM also provides [access to metadata](#extension-context-metadata) via the context, this information may not be sufficient. Executing arbitrary queries ensures maximum flexibility for extensions. Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest @@ -668,7 +717,7 @@ Needs: impl, utest, itest The BucketFS client in the extension context allows the extension definition to interact with BucketFS. Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest @@ -697,7 +746,7 @@ Rationale: * **Note:** EM will only need to work with Exasol v8, so support for v7 is actually not necessary. However the Docker container for v8 currently does not support running Python and Java UDFs. Until this is fixed we still need v7 for integration testing. Covers: -* [`req~install-extension-database-objects~1`](system_requirements.md#update-extension) +* [`req~install-extension-database-objects~1`](system_requirements.md#install-database-objects) Needs: impl, utest, itest diff --git a/doc/developer_guide.md b/doc/developer_guide.md index c6703e64..ee37a2f9 100644 --- a/doc/developer_guide.md +++ b/doc/developer_guide.md @@ -1,4 +1,6 @@ -# Developers Guide +# Developer Guide + +This guide describes how to develop, test and build the Extension Manager. ## Building @@ -36,42 +38,17 @@ If tracing fails with a `org.xml.sax.SAXParseException` you might need to run `m ## Testing -The different components of the project are responsible for testing different things. - -### extension-manager - The extension-manager project contains unit and integration tests that verify * Loading and executing of JavaScript extensions * Database interactions * REST API interface * Server-side parameter validation using `extension-parameter-validator` -* ... Tests use dummy extensions, no real extensions. -### Extensions - -Extensions are located in the repositories of the virtual schema implementations, e.g. `s3-document-files-virtual-schema`. - -Tests for extensions are: -* Verify correct implementation of a specific version of the `extension-manager-interface` using the TypeScript compiler -* Unit tests written in TypeScript verify all execution paths of the extension -* Integration tests written in Java use a specific version of the `extension-manager` to verify that the extension - * can be loaded - * can install a virtual schema and check that it works - * can update parameters of an existing virtual schema - * can upgrade a virtual schema created with an older version - * ... - -### Restrictions as Document Virtual Schemas Only Support a Single Version - -Document virtual schemas like `s3-document-files-virtual-schema` require a `SET SCRIPT` that must have a specific name. As this script references a specific virtual schema JAR archive, it is not possible to install multiple version of the same virtual schema in the same database `SCHEMA`. - -This means that in order to test a new version of a virtual schema, you need to create a new `SCHEMA` with the required database objects. - ### Non-Parallel Tests -The tests of this project use the exasol-test-setup-abstraction-server. There the tests connect to an Exasol database running in a docker container. For performance reasons the test-setup-abstraction reuses that container. This feature is not compatible with running tests in parallel. +The tests of this project use [`exasol-test-setup-abstraction-server`](https://github.com/exasol/exasol-test-setup-abstraction-server/). There the tests connect to an Exasol database running in a docker container. For performance reasons the test-setup-abstraction reuses that container. This feature is not compatible with running tests in parallel. Problems would be: @@ -93,6 +70,8 @@ To run only tests without the database use: go test -p 1 -short ./... ``` +Please note that also `-short` tests need `-p 1` because extension integration tests share a directory for building a test extension. Tests will fail randomly without `-p 1`. + ## Static Code Analysis ### Go Linter diff --git a/doc/extension_developer_guide.md b/doc/extension_developer_guide.md new file mode 100644 index 00000000..8f7dedfa --- /dev/null +++ b/doc/extension_developer_guide.md @@ -0,0 +1,85 @@ +# Extension Developer Guide + +This guide describes how to create and test an extension definition for the Extension Manager. + +## Extensions + +Definition and implementation of each extension are located in a common repository, e.g. `s3-document-files-virtual-schema`. + +The tests for an extensions usually will include +* Using the TypeScript compiler to verify correct implementation of a specific version of the `extension-manager-interface` +* Unit tests written in TypeScript to verify all execution paths of the extension's implementation. +* Integration tests written in Java using a specific version of the `extension-manager` to verify that the extension + * can be loaded + * can install a virtual schema and check that it works + * can update parameters of an existing virtual schema + * can upgrade a virtual schema created with an older version + * ... + +### Restrictions as Document-based Virtual Schemas Only Support a Single Version + +Document-based virtual schemas like `s3-document-files-virtual-schema` require a `SET SCRIPT` that must have a specific name. As this script references a specific virtual schema JAR archive, it is not possible to install multiple version of the same virtual schema in the same database schema. + +This means that in order to test a new version of a virtual schema, you need to create a new database schema with the required database objects. + +## Extension Manager Interface + +Extension definitions are written in TypeScript and compiled to a single JavaScript file. They implement the [extension-manager-interface](https://github.com/exasol/extension-manager-interface/). See [testing-extension](../extension-manager-integration-test-java/testing-extension) for an example including build scripts. + +## Extension Integration Test Framework for Java + +The Extension Integration Test Framework for Java (EITFJ) allows writing integration tests for extensions and their extension definitions. + +### Preconditions + +We assume your extension definition project is located in folder `$EXTENSION`. `$EXTENSION_ID` is the filename of your JavaScript extension definition. + +The project in `$EXTENSION` must fulfill the following preconditions: +* NPM modules are already installed to `node_modules` before running integration tests. +* `package.json` is configured to build the extension definition with `npm run build`. +* The build process writes the JavaScript file to `$EXTENSION/dist/$EXTENSION_ID` + +If your extension definition uses a different build process you can create a custom `ExtensionBuilder`. + +### Using EITFJ + +The EITFJ library is published to [Maven Central](https://central.sonatype.com/artifact/com.exasol/extension-manager-integration-test-java), so you can add it to your project as follows: + +```xml + + com.exasol + extension-manager-integration-test-java + $VERSION + test + +``` + +See [`ExampleIT.java`](../extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/ExampleIT.java) for an example of how to use EITFJ in your integration tests. Adapt the following constants depending to your own extension definition: + +* `EXTENSION_SOURCE_DIR`: relative path to the directory containing the extension definition sources (`$EXTENSION`) +* `EXTENSION_ID`: file name of the built JavaScript file (`$EXTENSION_ID`) + +Depending on the requirements of your extension you might also need to upload the adapter JAR or a JDBC driver to BucketFS in `@BeforeAll`. + +#### Features of class `ExtensionManagerSetup` + +Class `ExtensionManagerSetup` offers the following useful features: + +* `ExtensionManagerSetup.create()` downloads and starts the EM REST interface, builds your extension definition and adds it to EM's extension registry. +* Call `setup.client()` to get a client for the EM's REST interface. It allows you to install your extension, create a new instance etc. +* Call `setup.client().assertRequestFails()` to verify that a REST call fails with an expected status code and error message. This allows testing that your extension definitions throws an expected error. +* Call `setup.previousVersionManager()` to prepare a previous version of your extension. This is useful for testing the upgrade process. +* Call `setup.exasolMetadata()` to verify that expected database objects like `SCRIPT`, `CONNECTION` or `VIRTUAL SCHEMA` were created. +* Call `setup.addVirtualSchemaToCleanupQueue()` and `setup.addConnectionToCleanupQueue()` to delete a `CONNECTION` or `VIRTUAL SCHEMA` after a test. + +### EITFJ Configuration + +EITFJ works without additional configuration. During development you can however create file `extension-test.properties` to simplify local testing. We recommend adding this file to `.gitignore` to avoid accidentally committing it. + +`extension-test.properties` supports the following optional settings: + +* `localExtensionManager`: Path to a local clone of the `extension-manager` repository. This allows testing against a local version of extension manager that was not yet released. By default EITFJ will install extension manager using `go install`. +* `buildExtension`: Set this to `false` in order to skip building the extension definition before the tests. Use this to speedup tests when the extension definition is not modified. +* `buildExtensionManager`: Set this to `false` to skip building/installing the extension manager binary. Use this to speedup tests when extension manager is not modified. +* `extensionManagerVersion`: Version of EM to use during tests. By default EITFJ uses the same version as the version defined in `pom.xml` for `extension-manager-integration-test-java`. Changing this is not recommended. + diff --git a/doc/system_requirements.md b/doc/system_requirements.md index ac970f24..621d329c 100644 --- a/doc/system_requirements.md +++ b/doc/system_requirements.md @@ -22,7 +22,7 @@ Database Administrators (DBA) use EM through it's REST API or a user interface f ### Extension Developers -Extension developers create and maintain extensions for EM. +Extension developers create and maintain extensions for EM. They write integration tests for extensions and release and deploy extensions. ## Terms and Abbreviations @@ -133,7 +133,7 @@ An extension might consist of JDBC driver, artifacts, configuration and database Covers: * [`feat~install-extension~1`](#configure-an-extension) -* [`feat~configure-extension~1`](#updating-an-extension) +* [`feat~configure-extension~1`](#configure-an-extension) * [`feat~uninstall-extension~1`](#extensions) Needs: dsn @@ -193,7 +193,7 @@ Needs: dsn EM allows extensions to define a set of parameters. Each extension might have different parameters. Covers: -* [`feat~configure-extension~1`](#updating-an-extension) +* [`feat~configure-extension~1`](#configure-an-extension) Needs: dsn @@ -210,7 +210,7 @@ EM supports the following types for configuration parameters Rationale: EM's UI can then present all relevant parameters to the user and allow the user to assign a value to each parameter, e.g. enter credentials, select values from option lists. Covers -* [`feat~configure-extension~1`](#updating-an-extension) +* [`feat~configure-extension~1`](#configure-an-extension) Needs: dsn @@ -225,7 +225,7 @@ Rationale: * ensure security Covers: -* [`feat~configure-extension~1`](#updating-an-extension) +* [`feat~configure-extension~1`](#configure-an-extension) Needs: dsn @@ -283,6 +283,8 @@ The Extension API provides a defined interface that extensions need to implement Covers: * [`feat~developing-extensions~1`](#developing-extensions) +Needs: dsn + #### Integration Test Framework for Extensions `req~extension-testing-framework~1` @@ -291,6 +293,8 @@ The Extension Testing Framework contains common setup code and convenient helper Covers: * [`feat~developing-extensions~1`](#developing-extensions) +Needs: dsn + ## Non-functional Requirements ### UI Languages @@ -299,7 +303,7 @@ Covers: The current SaaS implementation only supports English as language in the user interface. To avoid complexity EM currently only supports English language in the user interface, too. This avoids additional efforts for UI translation until this is required. Covers: -* [`feat~configure-extension~1`](#updating-an-extension) +* [`feat~configure-extension~1`](#configure-an-extension) * [`feat~install-extension~1`](#configure-an-extension) * [`feat~uninstall-extension~1`](#extensions) diff --git a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerClient.java b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerClient.java index 5e5db9e3..370549ec 100644 --- a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerClient.java +++ b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerClient.java @@ -33,6 +33,7 @@ * The class also provides a convenient method {@link #assertRequestFails(Executable, Matcher, Matcher)} for verifying * that an API fails with expected error message and HTTP status code. */ +// [impl -> dsn~eitfj-access-extension-manager-rest-interface~1] public class ExtensionManagerClient { private static final Logger LOGGER = Logger.getLogger(ExtensionManagerClient.class.getName()); private final ExtensionApi extensionClient; diff --git a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerProcess.java b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerProcess.java index c5e7d712..d8af00c2 100644 --- a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerProcess.java +++ b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/ExtensionManagerProcess.java @@ -18,6 +18,7 @@ /** * This class allows starting and stopping an extension manager process. */ +// [impl -> dsn~eitfj-start-extension-manager~1] class ExtensionManagerProcess implements AutoCloseable { private static final Logger LOGGER = Logger.getLogger(ExtensionManagerProcess.class.getName()); private static final Duration SERVER_STARTUP_TIMEOUT = Duration.ofSeconds(5); diff --git a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/PreviousExtensionVersion.java b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/PreviousExtensionVersion.java index 1b6ad6a1..ce2d50d6 100644 --- a/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/PreviousExtensionVersion.java +++ b/extension-manager-integration-test-java/src/main/java/com/exasol/extensionmanager/itest/PreviousExtensionVersion.java @@ -12,6 +12,7 @@ /** * This represents a previous version of an extension. */ +// [impl -> dsn~eitfj-prepare-previous-extension-version~1] public class PreviousExtensionVersion { private static final Logger LOGGER = Logger.getLogger(PreviousExtensionVersion.class.getName()); private final ExtensionManagerSetup setup; diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/ExampleIT.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/ExampleIT.java new file mode 100644 index 00000000..217fbd43 --- /dev/null +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/ExampleIT.java @@ -0,0 +1,54 @@ +package com.exasol.extensionmanager; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.jupiter.api.*; + +import com.exasol.exasoltestsetup.ExasolTestSetup; +import com.exasol.exasoltestsetup.ExasolTestSetupFactory; +import com.exasol.extensionmanager.client.model.ExtensionsResponseExtension; +import com.exasol.extensionmanager.itest.ExtensionManagerSetup; +import com.exasol.extensionmanager.itest.builder.ExtensionBuilder; + +/** + * This integration test illustrates the usage of {@link ExtensionManagerSetup}. See the + * Extension Developer + * Guide for details. + */ +// [doc -> dsn~eitfj-start-extension-manager~1] +// [doc -> dsn~eitfj-access-extension-manager-rest-interface~1] +class ExampleIT { + private static final Path EXTENSION_SOURCE_DIR = Paths.get("testing-extension").toAbsolutePath(); + private static final String EXTENSION_ID = "testing-extension.js"; + private static ExasolTestSetup exasolTestSetup; + private static ExtensionManagerSetup setup; + + @BeforeAll + static void setup() { + exasolTestSetup = new ExasolTestSetupFactory(Path.of("cloud-setup")).getTestSetup(); + setup = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( + EXTENSION_SOURCE_DIR, EXTENSION_SOURCE_DIR.resolve("dist").resolve(EXTENSION_ID))); + } + + @AfterAll + static void teardown() throws Exception { + setup.close(); + exasolTestSetup.close(); + } + + @AfterEach + void cleanup() { + setup.cleanup(); + } + + @Test + void listExtensions() { + final List extensions = setup.client().getExtensions(); + assertThat(extensions, hasSize(1)); + } +} diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExasolMetadataIT.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExasolMetadataIT.java index 1bcaa308..8450598d 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExasolMetadataIT.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExasolMetadataIT.java @@ -1,5 +1,6 @@ package com.exasol.extensionmanager.itest; +import static com.exasol.extensionmanager.itest.IntegrationTestCommon.BUILT_EXTENSION_JS; import static com.exasol.extensionmanager.itest.IntegrationTestCommon.TESTING_EXTENSION_SOURCE_DIR; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -17,8 +18,8 @@ class ExasolMetadataIT { @BeforeAll static void setupExasol() { exasolTestSetup = IntegrationTestCommon.createExasolTestSetup(); - extensionManager = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( - TESTING_EXTENSION_SOURCE_DIR, TESTING_EXTENSION_SOURCE_DIR.resolve("dist/testing-extension.js"))); + extensionManager = ExtensionManagerSetup.create(exasolTestSetup, + ExtensionBuilder.createDefaultNpmBuilder(TESTING_EXTENSION_SOURCE_DIR, BUILT_EXTENSION_JS)); metadata = extensionManager.exasolMetadata(); } diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientIT.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientIT.java index e2629578..18dbb458 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientIT.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientIT.java @@ -1,6 +1,6 @@ package com.exasol.extensionmanager.itest; -import static com.exasol.extensionmanager.itest.IntegrationTestCommon.TESTING_EXTENSION_SOURCE_DIR; +import static com.exasol.extensionmanager.itest.IntegrationTestCommon.*; import static java.util.Collections.emptyList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; @@ -21,9 +21,10 @@ import com.exasol.extensionmanager.client.model.*; import com.exasol.extensionmanager.itest.builder.ExtensionBuilder; +// [itest -> dsn~eitfj-access-extension-manager-rest-interface~1] +// [itest -> dsn~eitfj-start-extension-manager~1] class ExtensionManagerClientIT { - private static final String EXTENSION_ID = "testing-extension.js"; private static final String EXTENSION_VERSION = "0.0.0"; private static ExasolTestSetup exasolTestSetup; private static Connection connection; @@ -35,8 +36,8 @@ static void setup() throws SQLException, IOException { exasolTestSetup = IntegrationTestCommon.createExasolTestSetup(); connection = exasolTestSetup.createConnection(); createTestConfigFile(); - extensionManager = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( - TESTING_EXTENSION_SOURCE_DIR, TESTING_EXTENSION_SOURCE_DIR.resolve("dist").resolve(EXTENSION_ID))); + extensionManager = ExtensionManagerSetup.create(exasolTestSetup, + ExtensionBuilder.createDefaultNpmBuilder(TESTING_EXTENSION_SOURCE_DIR, BUILT_EXTENSION_JS)); client = extensionManager.client(); } diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientTest.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientTest.java index 0ea16f23..3ee31a35 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientTest.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerClientTest.java @@ -20,6 +20,7 @@ import jakarta.json.JsonObject; +// [utest -> dsn~eitfj-access-extension-manager-rest-interface~1] @ExtendWith(MockitoExtension.class) class ExtensionManagerClientTest { @@ -42,7 +43,6 @@ void setup() { dbConnectionInfoMock = new SqlConnectionInfo(DB_HOST, DB_PORT, DB_USER, DB_PASSWORD); testee = new ExtensionManagerClient(extensionClientMock, installationApiMock, instanceClientMock, dbConnectionInfoMock); - } @Test diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerSetupIT.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerSetupIT.java index 8a99d616..bd024853 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerSetupIT.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/ExtensionManagerSetupIT.java @@ -1,5 +1,6 @@ package com.exasol.extensionmanager.itest; +import static com.exasol.extensionmanager.itest.IntegrationTestCommon.BUILT_EXTENSION_JS; import static com.exasol.extensionmanager.itest.IntegrationTestCommon.TESTING_EXTENSION_SOURCE_DIR; import static com.exasol.matcher.ResultSetStructureMatcher.table; import static org.hamcrest.MatcherAssert.assertThat; @@ -34,8 +35,8 @@ static void teardownExasol() throws Exception { @BeforeEach void setup() throws SQLException { - extensionManager = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( - TESTING_EXTENSION_SOURCE_DIR, TESTING_EXTENSION_SOURCE_DIR.resolve("dist/testing-extension.js"))); + extensionManager = ExtensionManagerSetup.create(exasolTestSetup, + ExtensionBuilder.createDefaultNpmBuilder(TESTING_EXTENSION_SOURCE_DIR, BUILT_EXTENSION_JS)); } @AfterEach diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/IntegrationTestCommon.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/IntegrationTestCommon.java index 9dac19c0..20392451 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/IntegrationTestCommon.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/IntegrationTestCommon.java @@ -9,6 +9,8 @@ class IntegrationTestCommon { static final Path TESTING_EXTENSION_SOURCE_DIR = Paths.get("testing-extension").toAbsolutePath(); static final Path CONFIG_FILE = Paths.get("extension-test.properties").toAbsolutePath(); + static final String EXTENSION_ID = "testing-extension.js"; + static final Path BUILT_EXTENSION_JS = TESTING_EXTENSION_SOURCE_DIR.resolve("dist").resolve(EXTENSION_ID); private IntegrationTestCommon() { // Not instantiable diff --git a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/PreviousExtensionVersionTest.java b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/PreviousExtensionVersionTest.java index a91c21ae..e22e6f8d 100644 --- a/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/PreviousExtensionVersionTest.java +++ b/extension-manager-integration-test-java/src/test/java/com/exasol/extensionmanager/itest/PreviousExtensionVersionTest.java @@ -17,6 +17,7 @@ import com.exasol.extensionmanager.client.model.UpgradeExtensionResponse; +// [utest -> dsn~eitfj-prepare-previous-extension-version~1] @ExtendWith(MockitoExtension.class) class PreviousExtensionVersionTest { diff --git a/pkg/extensionAPI/extensionApi.go b/pkg/extensionAPI/extensionApi.go index a3693007..05e2aa1e 100644 --- a/pkg/extensionAPI/extensionApi.go +++ b/pkg/extensionAPI/extensionApi.go @@ -69,6 +69,8 @@ func loadExtension(vm *goja.Runtime, id, content string) (*installedExtension, e return &extension, nil } +// installedExtension allows deserializing extension definitions that implement the extension-manager-interface (https://github.com/exasol/extension-manager-interface/). +/* [impl -> dsn~extension-api~1]. */ type installedExtension struct { Extension rawJsExtension `json:"extension"` APIVersion string `json:"apiVersion"` diff --git a/pkg/extensionAPI/extensionApi_test.go b/pkg/extensionAPI/extensionApi_test.go index f43f13a0..b62a784b 100644 --- a/pkg/extensionAPI/extensionApi_test.go +++ b/pkg/extensionAPI/extensionApi_test.go @@ -42,6 +42,7 @@ func (suite *ExtensionApiSuite) TearDownTest() { } /* [utest -> dsn~extension-definition~1] */ +/* [utest -> dsn~extension-api~1]. */ func (suite *ExtensionApiSuite) TestLoadExtension() { extensionContent := integrationTesting.CreateTestExtensionBuilder(suite.T()).Build().AsString() extension := suite.loadExtension(extensionContent) diff --git a/pkg/integrationTesting/testExtensionBuilder.go b/pkg/integrationTesting/testExtensionBuilder.go index 4ccf7e21..5efea75a 100644 --- a/pkg/integrationTesting/testExtensionBuilder.go +++ b/pkg/integrationTesting/testExtensionBuilder.go @@ -15,7 +15,7 @@ import ( log "github.com/sirupsen/logrus" ) -const defaultExtensionApiVersion = "0.2.0" +const defaultExtensionApiVersion = "0.3.0" func CreateTestExtensionBuilder(t *testing.T) *TestExtensionBuilder { builder := TestExtensionBuilder{testing: t} @@ -240,12 +240,12 @@ func (builder TestExtensionBuilder) runNpmInstall(workDir string) { // running "npm install" once for each version is enough return } - builder.testing.Logf("Running npm install in %s", workDir) + builder.testing.Logf("Running 'npm install in %s", workDir) installCommand := exec.Command("npm", "install") installCommand.Dir = workDir output, err := installCommand.CombinedOutput() if err != nil { - log.Fatalf("Failed to run 'npm install' in dir %v. Cause: %v, Output:\n%s", workDir, err, output) + log.Fatalf("Failed to run 'npm install' in dir %v.\nEnsure to use flag '-p 1' when starting tests.\nCause: %v, Output:\n%s", workDir, err, output) } else { isNpmInstallCalledForVersion[builder.extensionApiVersion] = true }