diff --git a/README.md b/README.md index 0513799..3413199 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,249 @@ [![latest-release](https://img.shields.io/github/release/utPLSQL/utPLSQL-maven-plugin.svg)](https://github.com/utPLSQL/utPLSQL-maven-plugin/releases) -[![license](https://img.shields.io/github/license/utPLSQL/utPLSQL-cli.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![license](https://img.shields.io/github/license/utPLSQL/utPLSQL-maven-plugin.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![Build](https://github.com/utPLSQL/utPLSQL-maven-plugin/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/utPLSQL/utPLSQL-maven-plugin/actions/workflows/build.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=utPLSQL_utPLSQL-maven-plugin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=utPLSQL_utPLSQL-maven-plugin) # utPLSQL-maven-plugin -A maven plugin for running Unit Tests with utPLSQL v3+. +A Maven plugin that runs [utPLSQL](https://www.utplsql.org/) v3 unit tests in an Oracle database as part of the Maven +`test` phase, and writes test results and code coverage reports that CI servers and tools such as SonarQube can read. + +For writing tests, reporters and code coverage, see the [utPLSQL documentation](https://www.utplsql.org/utPLSQL/latest/). ## Compatibility -This plugin is compatible with utPLSQL 3.1.0+. +The plugin works with utPLSQL 3.1.0 or newer installed in the database. ## Prerequisites -* Java SE Runtime Environment 17 -* Maven Version 3.9.9+ +* Java 17 or newer +* Maven 3.9.9 or newer + +## Quick start + +Add the plugin to your `pom.xml` and provide the database connection: + +```xml + + jdbc:oracle:thin:@//localhost:1521/FREEPDB1 + app + app_password + + + + + + org.utplsql + utplsql-maven-plugin + 3.2.0 + + + + test + + + + + + +``` + +Then run: + +```bash +mvn test +``` + +With no further configuration, the plugin: + +* runs all test suites in the schema of the connected user, +* maps source files from `src/main/plsql` (`**/*.*`) and test files from `src/test/plsql` (`**/*.pkg`) to database + objects for coverage reporting, +* prints results to the console with `UT_DOCUMENTATION_REPORTER`, +* fails the build when any test fails or errors. + +## Database connection + +The connection is configured with three properties. Each can be set in the `pom.xml` or passed on the command line: + +| Property | Description | Example | +|----------|------------------------------------------------------------------------------|---------------------------------------------| +| `dbUrl` | JDBC URL of the database | `-DdbUrl=jdbc:oracle:thin:@//host:1521/svc` | +| `dbUser` | Database user; leave unset when using an [Oracle Wallet](#oracle-wallet-secure-external-password-store) | `-DdbUser=app` | +| `dbPass` | Password of `dbUser` | `-DdbPass=app_password` | + +```bash +mvn test -DdbUrl=jdbc:oracle:thin:@//localhost:1521/FREEPDB1 -DdbUser=app -DdbPass=app_password +``` + +### Oracle Wallet (Secure External Password Store) + +To keep the database password out of the `pom.xml` and the command line, store the credentials in an Oracle Wallet, +leave `dbUser` and `dbPass` unset, and point `dbUrl` to the TNS alias of the stored credential: + +```xml + + jdbc:oracle:thin:@MYDATABASE + +``` + +Setup example: -## Usage +```bash +# create an auto-login wallet with credentials for TNS alias MYDATABASE +orapki wallet create -wallet $HOME/oracle/wallet -auto_login_local +mkstore -wrl $HOME/oracle/wallet -createCredential MYDATABASE someusername -### Skipping Tests +# point the JDBC driver to the wallet +echo "oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=$HOME/oracle/wallet)))" \ + > $HOME/oracle/network/admin/ojdbc.properties -To skip running the tests for a particular project, set the **skipUtplsqlTests** property to true. +# tnsnames.ora with the MYDATABASE entry must be in the same directory +export TNS_ADMIN=$HOME/oracle/network/admin +``` + +The JDBC driver looks for `tnsnames.ora` and `ojdbc.properties` in the directory given by, in order of precedence: + +1. the `TNS_ADMIN` parameter in the URL, e.g. `jdbc:oracle:thin:@MYDATABASE?TNS_ADMIN=/path/to/network/admin` +2. the Java system property `oracle.net.tns_admin`, e.g. `export MAVEN_OPTS="-Doracle.net.tns_admin=/path/to/network/admin"` +3. the `TNS_ADMIN` environment variable + +The TNS alias used in `dbUrl` must match the alias of the credential stored in the wallet. + +## Selecting tests + +By default, all suites in the schema of the connected user are run. Use `paths` to run specific schemas, suites, +packages or procedures, and `tags` to run only tests with the given tags: ```xml - true + + app + app:com.my_org.my_project + + + fast + ``` -You can also skip the tests via the command line by executing the following command: +A path has one of the formats `schema[.package[.procedure]]` or `schema:suite[.suite[.suite][...]][.procedure]`, +and both formats can be mixed. See [Running tests](https://www.utplsql.org/utPLSQL/latest/userguide/running-unit-tests.html) +and [Run by tags](https://www.utplsql.org/utPLSQL/latest/userguide/running-unit-tests.html#run-by-tags) in the utPLSQL +documentation. - mvn install -DskipTests +To detect hidden dependencies between tests, run them in +[random order](https://www.utplsql.org/utPLSQL/latest/userguide/running-unit-tests.html#random-order) with +`randomTestOrder`. Set `randomTestOrderSeed` to repeat a particular order. -#### Skipping by Default +## Reporters -If you want to skip tests by default but want the ability to re-enable tests from the command line, you need to go via a -properties section in the pom: +Reporters decide the format of the results. Each reporter writes to a file, to the console, or both: ```xml - true + + + UT_DOCUMENTATION_REPORTER + + + UT_SONAR_TEST_REPORTER + utplsql/sonar-test-report.xml + + + UT_COVERAGE_SONAR_REPORTER + utplsql/coverage-sonar-report.xml + true + + ``` -This will allow you to run with all tests disabled by default and to run them with this command: +* `name` is the name of a utPLSQL reporter (case-insensitive). Custom reporters installed in the database can be used + as well. +* `fileOutput` is the report file. Relative paths are resolved against the build directory (`target` by default). +* `consoleOutput` prints the report to the console. It defaults to `true` when no `fileOutput` is given, and to `false` + otherwise. +* Without any `reporters`, `UT_DOCUMENTATION_REPORTER` prints to the console. + +Reporters provided by utPLSQL (see [Reporters](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html) for +details and the reporters available in your version): + +| Reporter | Output | +|----------------------------------|--------------------------------------------------------------------------------------------------------| +| `UT_DOCUMENTATION_REPORTER` | [Human-readable test results](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#documentation-reporter) | +| `UT_JUNIT_REPORTER` | [JUnit XML](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#junit-reporter) | +| `UT_TFS_JUNIT_REPORTER` | [JUnit XML for TFS / Azure DevOps](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#tfs-vsts-reporter) | +| `UT_TEAMCITY_REPORTER` | [TeamCity service messages](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#teamcity-reporter) | +| `UT_SONAR_TEST_REPORTER` | [SonarQube generic test execution](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#sonar-test-reporter) | +| `UT_TAP_REPORTER` | [Test Anything Protocol](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#tap-reporter) | +| `UT_DEBUG_REPORTER` | [Diagnostic output of the test run](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#debug-reporter) | +| `UT_COVERAGE_HTML_REPORTER` | [Code coverage as HTML](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#coverage-reporters) | +| `UT_COVERAGE_SONAR_REPORTER` | [Code coverage for SonarQube](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#coverage-reporters) | +| `UT_COVERAGE_COBERTURA_REPORTER` | [Code coverage in Cobertura format](https://www.utplsql.org/utPLSQL/latest/userguide/reporters.html#coverage-reporters) | + +## Code coverage + +Code coverage is gathered whenever a coverage reporter is configured. How utPLSQL gathers and reports coverage is +described in [Coverage](https://www.utplsql.org/utPLSQL/latest/userguide/coverage.html). + +### Choosing the objects to report + +| Parameter | Description | +|---------------------|--------------------------------------------------------------------------------------| +| `includeObject` | Comma-separated list of objects to include, format `[schema.]object[,[schema.]object ...]` | +| `excludeObject` | Comma-separated list of objects to exclude, same format as `includeObject` | +| `includeSchemaExpr` | Regular expression for the names of schemas to include | +| `excludeSchemaExpr` | Regular expression for the names of schemas to exclude | +| `includeObjectExpr` | Regular expression for the names of objects to include | +| `excludeObjectExpr` | Regular expression for the names of objects to exclude | + +The regular expression filters need a utPLSQL version that supports them. See +[Coverage reporting options](https://www.utplsql.org/utPLSQL/latest/userguide/coverage.html#coverage-reporting-options). + +### Mapping project files to database objects + +For [project based coverage](https://www.utplsql.org/utPLSQL/latest/userguide/coverage.html#project-based-coverage), +utPLSQL reports coverage per source file instead of per database object. The plugin passes the files found by +`sources` and `tests` to utPLSQL, which maps each file to a database object by its path: + +* `sources` / `tests` select the files (`directory` and `includes`). They default to `src/main/plsql` with `**/*.*` and + `src/test/plsql` with `**/*.pkg`. +* `sourcesOwner` / `testsOwner` set the schema owning the objects. +* `sourcesRegexExpression`, `sourcesOwnerSubexpression`, `sourcesNameSubexpression` and `sourcesTypeSubexpression` + (and their `tests...` counterparts) describe how the owner, name and type are read from the file path. +* `sourcesCustomTypeMapping` / `testsCustomTypeMapping` map directory names or file extensions to object types. + +## Skipping tests + +Set `skipUtplsqlTests` to `true` in the plugin configuration or on the command line: + +```bash +mvn install -DskipUtplsqlTests=true +``` + +Maven's `-DskipTests` does not skip utPLSQL tests. + +To skip the tests by default and enable them only when needed, set the property in the `pom.xml`: + +```xml + + true + +``` + +and override it on the command line: - mvn install -DskipUtplsqlTests=false +```bash +mvn install -DskipUtplsqlTests=false +``` -### Configuration +To run all tests but not fail the build on test failures, set `ignoreFailure` to `true`, or pass Maven's standard +`-Dmaven.test.failure.ignore=true`. -Please refer to the following usage example for the parameters descriptions: +## Configuration reference + +All parameters are optional: ```xml 1.0.0 - - - - url_of_connection - - - user - - - password + + jdbc:oracle:thin:@//localhost:1521/FREEPDB1 + + app + + app_password @@ -79,142 +269,101 @@ Please refer to the following usage example for the parameters descriptions: org.utplsql utplsql-maven-plugin - 3.1.11 + 3.2.0 test - - - - - - - - - - + + + - schema_name + app - - - - src/test/resources/scripts/sources - - - **/*pkg - **/*pkb - - - - - - - - src/test/resources/scripts/test - - - **/*pkg - **/*pkb - - - + + + fast + - + + + + true + 5 - - + + false - - - false - - - + + false - - + + + false + + + false - - + + 0 - - - test_tag - - - - true - - 5 - - - - - app.test1,app.test2 - - - - - app.test1,app.test2 - - - app.test1,app.test2 - - - app.test1,app.test2 - - - app.test1,app.test2 - - - app.test1,app.test2 - - - - - - - - - - - - - + + - UT_COVERAGE_SONAR_REPORTER - - utplsql/coverage-sonar-reporter.xml - - true + UT_DOCUMENTATION_REPORTER UT_SONAR_TEST_REPORTER - utplsql/sonar-test-reporter.xml + + utplsql/sonar-test-report.xml + false - UT_TEAMCITY_REPORTER + UT_COVERAGE_SONAR_REPORTER + utplsql/coverage-sonar-report.xml - - - code_owner - .*/\w+/(\w+)/(\w+)\.\w{3} - app - 2 - 1 + + + app.pkg_orders,app.pkg_customers + app.pkg_logging + + + + ^APP$ + ^APP_TEST$ + ^PKG_ + _TMP$ + + + + + + src/main/plsql + + **/*.pks + **/*.pkb + + + + + + + + .*/(\w+)/(\w+)/(\w+)\.\w{3} + 1 + 2 + 3 package body @@ -222,13 +371,23 @@ Please refer to the following usage example for the parameters descriptions: - - - tests_owner - .*/\w+/(\w+)/(\w+)\.\w{3} - test - 2 - 1 + + + + + src/test/plsql + + **/*.pks + **/*.pkb + + + + + + .*/(\w+)/(\w+)/(\w+)\.\w{3} + 1 + 2 + 3 package body @@ -244,46 +403,71 @@ Please refer to the following usage example for the parameters descriptions: ``` -More project samples are available in the `src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT` -directory: - -* **simple-project:** Minimalist test project with standard project directory structure. -* **regex-project:** Overrides project directory structure and use additional parameters (sourcesRegexExpression, - testsRegexExpression, ...), to tell utPLSQL how project files should be mapped into database objects. -* **type-mapping-project:** Example how to use regex and custom type parameters together. -* **owner-param-project:** Demonstrates how to use sourcesOwner and testsOwner parameters. - -## Comparison with utPLSQL CLI - -| CLI short parameter | CLI long parameter | Maven XML path | -|----------------------|----------------------------|--------------------------------------------------| -| -c | --color | | -| | --failure-exit-code | | -| -p | --path | paths.path | -| -f | --format | reporters.reporter.name | -| -o | | reporters.reporter.fileOutput | -| -s | | reporters.reporter.consoleOutput | -| | | ignoreFailure | -| -scc | --skip-compatibility-check | skipCompatibilityCheck | -| | --tags | tags.tag | -| -D | --dbms_output | dbmsOutput | -| -r | --random-test-order | randomTestOrder | -| -seed | --random-test-order-seed | randomTestOrderSeed | -| -exclude | | excludeObject | -| -include | | includeObject | -| | | | -| -source_path | | sources.source.directory | -| -owner | | sourcesOwner | -| -regex_expression | | sourcesRegexExpression | -| -type_mapping | | list of testsCustomTypeMapping.customTypeMapping | -| -owner_subexpression | | sourcesOwnerSubexpression | -| -type_subexpression | | sourcesTypeSubexpression | -| -name_subexpression | | sourcesNameSubexpression | -| | | | -| -test_path | | tests.test.directory | -| -owner | | testsOwner | -| -regex_expression | | testsRegexExpression | -| -type_mapping | | list of testsCustomTypeMapping.customTypeMapping | -| -owner_subexpression | | testsOwnerSubexpression | -| -type_subexpression | | testsTypeSubexpression | -| -name_subexpression | | testsNameSubexpression | +## Sample projects + +The plugin's integration tests double as examples, in +[`src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT): + +* [`minimalist`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/minimalist): no plugin configuration, + only the connection. +* [`simple`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple): standard project directory + structure with Sonar reporters. +* [`regex`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/regex): custom directory structure, mapped + to database objects with `sourcesRegexExpression`, `testsRegexExpression` and related parameters. +* [`type_mapping`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/type_mapping): regular expressions + combined with custom type mappings. +* [`owner_param`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param): `sourcesOwner` and + `testsOwner`. +* [`tags`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/tags): running tests by tag. +* [`include_object`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/include_object), + [`exclude_object`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/exclude_object), + [`include_object_expr`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/include_object_expr) and + [`exclude_object_expr`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/exclude_object_expr): + choosing the objects in the coverage report. +* [`ora_stuck_timeout`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/ora_stuck_timeout): + `oraStuckTimeout`. +* [`skip`](src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/skip): `skipUtplsqlTests`. + +## Comparison with utPLSQL-cli + +[utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli) runs the same tests from the command line. The table maps its +[`run` command options](https://github.com/utPLSQL/utPLSQL-cli#run) to the plugin configuration: + +| utPLSQL-cli option | Maven configuration | +|------------------------------------|----------------------------------------------------------| +| `` | `dbUrl`, `dbUser`, `dbPass` | +| `-p`, `--path` | `paths.path` | +| `--tags` | `tags.tag` | +| `-f`, `--format` | `reporters.reporter.name` | +| `-o` | `reporters.reporter.fileOutput` | +| `-s` | `reporters.reporter.consoleOutput` | +| `-c`, `--color` | follows Maven's console color setting | +| `--failure-exit-code` | not available; use `ignoreFailure` to not fail the build | +| `-scc`, `--skip-compatibility-check` | `skipCompatibilityCheck` | +| `-D`, `--dbms_output` | `dbmsOutput` | +| `-r`, `--random-test-order` | `randomTestOrder` | +| `-seed`, `--random-test-order-seed` | `randomTestOrderSeed` | +| `--ora-stuck-timeout` | `oraStuckTimeout` | +| `-include` | `includeObject` | +| `-exclude` | `excludeObject` | +| not available | `includeSchemaExpr`, `excludeSchemaExpr`, `includeObjectExpr`, `excludeObjectExpr` | +| not available | `skipUtplsqlTests` | +| `--coverage-schemes` | not available | +| `-t`, `--timeout` | not available | +| `-source_path` | `sources.source.directory` | +| `-owner` | `sourcesOwner` | +| `-regex_expression` | `sourcesRegexExpression` | +| `-type_mapping` | `sourcesCustomTypeMapping.customTypeMapping` | +| `-owner_subexpression` | `sourcesOwnerSubexpression` | +| `-type_subexpression` | `sourcesTypeSubexpression` | +| `-name_subexpression` | `sourcesNameSubexpression` | +| `-test_path` | `tests.test.directory` | +| `-owner` | `testsOwner` | +| `-regex_expression` | `testsRegexExpression` | +| `-type_mapping` | `testsCustomTypeMapping.customTypeMapping` | +| `-owner_subexpression` | `testsOwnerSubexpression` | +| `-type_subexpression` | `testsTypeSubexpression` | +| `-name_subexpression` | `testsNameSubexpression` | + +In utPLSQL-cli, `-owner`, `-regex_expression` and the other mapping options apply to the preceding `-source_path` or +`-test_path`. diff --git a/pom.xml b/pom.xml index 7dca8e9..7bd616e 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ UTF-8 17 - 3.2.4 + 3.2.5 3.9.9 @@ -82,6 +82,24 @@ + + + + + + org.codehaus.plexus + plexus-utils + 3.6.1 + + + + org.assertj + assertj-core + 3.27.7 + + + + @@ -99,7 +117,7 @@ org.apache.maven.shared maven-shared-utils - 3.4.2 + 3.5.0 @@ -128,12 +146,6 @@ ${junit.version} test - - org.junit.vintage - junit-vintage-engine - ${junit.version} - test - com.soebes.itf.jupiter.extension diff --git a/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoConnectionTest.java b/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoConnectionTest.java new file mode 100644 index 0000000..ce2d941 --- /dev/null +++ b/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoConnectionTest.java @@ -0,0 +1,116 @@ +package org.utplsql.maven.plugin; + +import oracle.security.pki.OracleSecretStore; +import oracle.security.pki.OracleWallet; +import org.apache.maven.api.plugin.testing.Basedir; +import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.api.plugin.testing.MojoParameter; +import org.apache.maven.api.plugin.testing.MojoTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.utplsql.maven.plugin.UtPlsqlMojoTest.UNIT_TESTS; + +/** + * Tests of the database connection configuration. + *

+ * Unlike {@link UtPlsqlMojoTest}, no connection is given as mojo parameters, + * so the mojo falls back to the dbUrl, dbUser and dbPass system properties. + */ +@MojoTest +@MojoParameter(name = "targetDir", value = "target/unit-tests") +class UtPlsqlMojoConnectionTest { + + private static final String WALLET_TNS_ALIAS = "UTPLSQL_MAVEN_WALLET"; + + @TempDir + Path tempDir; + + @AfterEach + void clearSystemProperties() { + System.clearProperty("dbUrl"); + System.clearProperty("dbUser"); + System.clearProperty("dbPass"); + } + + /** + * DB configuration from System Properties + *

+ * Given : a pom.xml without dbUrl, dbUser and dbPass configured + * When : pom is read + * Then : System Properties must be used to configure database + */ + @Test + @Basedir(UNIT_TESTS + "db_config_using_system_properties") + void db_config_using_system_properties(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { + System.setProperty("dbUrl", "jdbc:oracle:thin:@//localhost:1521/FREEPDB1"); + System.setProperty("dbUser", "UT3"); + System.setProperty("dbPass", "ut3"); + + utPlsqlMojo.execute(); + } + + /** + * Connection using Oracle Wallet (Secure External Password Store) + *

+ * Given : a pom.xml without dbUser and dbPass and a dbUrl pointing to a TNS alias stored in an Oracle Wallet + * When : tests are run + * Then : credentials are taken from the wallet + */ + @Test + @Basedir(UNIT_TESTS + "wallet_connection") + void wallet_connection_without_credentials(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { + System.setProperty("dbUrl", walletDbUrl()); + + utPlsqlMojo.execute(); + } + + /** + * Connection using Oracle Wallet with empty credentials + *

+ * Given : a dbUrl pointing to a TNS alias stored in an Oracle Wallet and empty dbUser and dbPass (e.g. -DdbUser= -DdbPass=) + * When : tests are run + * Then : empty credentials are ignored and credentials are taken from the wallet + */ + @Test + @Basedir(UNIT_TESTS + "wallet_connection") + void wallet_connection_with_empty_credentials(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { + System.setProperty("dbUrl", walletDbUrl()); + System.setProperty("dbUser", ""); + System.setProperty("dbPass", ""); + + utPlsqlMojo.execute(); + } + + /** + * Creates an auto-login Oracle Wallet holding the UT3 credentials for {@link #WALLET_TNS_ALIAS}, + * with tnsnames.ora and ojdbc.properties next to it, so no Oracle client tooling (mkstore/orapki) is needed. + * + * @return JDBC URL of the TNS alias, with the generated directory as TNS_ADMIN + */ + private String walletDbUrl() throws Exception { + Path tnsAdmin = Files.createDirectories(tempDir.resolve("tns_admin")); + + OracleWallet wallet = new OracleWallet(); + wallet.create("Wallet_Pwd_123".toCharArray()); + OracleSecretStore secretStore = wallet.getSecretStore(); + secretStore.createCredential(WALLET_TNS_ALIAS.toCharArray(), "UT3".toCharArray(), "ut3".toCharArray()); + wallet.setSecretStore(secretStore); + wallet.saveAs(tnsAdmin.toString()); + wallet.createSSO(); + wallet.saveSSO(); + + Files.writeString(tnsAdmin.resolve("tnsnames.ora"), + WALLET_TNS_ALIAS + " = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))" + + "(CONNECT_DATA = (SERVICE_NAME = FREEPDB1)))\n"); + // ${TNS_ADMIN} is resolved by the JDBC driver, the same way as in wallets downloaded for Oracle Autonomous Database + Files.writeString(tnsAdmin.resolve("ojdbc.properties"), + "oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=${TNS_ADMIN})))\n"); + + return "jdbc:oracle:thin:@" + WALLET_TNS_ALIAS + "?TNS_ADMIN=" + tnsAdmin.toAbsolutePath().toString().replace('\\', '/'); + } +} diff --git a/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoTest.java b/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoTest.java index 3e144be..e0df9e7 100644 --- a/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoTest.java +++ b/src/test/java/org/utplsql/maven/plugin/UtPlsqlMojoTest.java @@ -1,27 +1,37 @@ package org.utplsql.maven.plugin; +import org.apache.maven.api.plugin.testing.Basedir; +import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.api.plugin.testing.MojoParameter; +import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.testing.MojoRule; -import org.junit.Rule; -import org.junit.Test; +import org.apache.maven.plugin.logging.SystemStreamLog; +import org.junit.jupiter.api.Test; import org.utplsql.api.FileMapperOptions; import org.utplsql.maven.plugin.model.ReporterParameter; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; +import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -public class UtPlsqlMojoTest { - - @Rule - public final MojoRule rule = new MojoRule(); +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests of the mojo configured from the pom.xml files in {@value #UNIT_TESTS}. + *

+ * The test harness does not resolve pom properties, so the database connection is given as mojo parameters. + * Reports are written to the build directory instead of next to the test poms. + */ +@MojoTest +@MojoParameter(name = "url", value = "jdbc:oracle:thin:@//localhost:1521/FREEPDB1") +@MojoParameter(name = "user", value = "UT3") +@MojoParameter(name = "password", value = "ut3") +@MojoParameter(name = "targetDir", value = "target/unit-tests") +class UtPlsqlMojoTest { + + static final String UNIT_TESTS = "src/test/resources/unit-tests/"; /** * Invalid Sources Directory @@ -31,10 +41,8 @@ public class UtPlsqlMojoTest { * Then : it should throw a MojoExecutionException */ @Test - public void invalid_sources_directory() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("invalid_sources_directory"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "invalid_sources_directory") + void invalid_sources_directory(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) { MojoExecutionException exception = assertThrows(MojoExecutionException.class, utPlsqlMojo::execute); assertEquals("Directory foo does not exist!", exception.getMessage()); @@ -48,10 +56,8 @@ public void invalid_sources_directory() throws Exception { * Then : it should throw a MojoExecutionException */ @Test - public void invalid_tests_directory() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("invalid_tests_directory"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "invalid_tests_directory") + void invalid_tests_directory(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) { MojoExecutionException exception = assertThrows(MojoExecutionException.class, utPlsqlMojo::execute); assertEquals("Directory bar does not exist!", exception.getMessage()); @@ -65,10 +71,8 @@ public void invalid_tests_directory() throws Exception { * Then : it should fill all parameters correctly */ @Test - public void sources_tests_parameters() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("sources_tests_parameters"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "sources_tests_parameters") + void sources_tests_parameters(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { assertEquals(2, utPlsqlMojo.reporters.size()); // check sources @@ -107,10 +111,8 @@ public void sources_tests_parameters() throws Exception { * Then : it should not find any source files */ @Test - public void sources_and_tests_parameter_does_not_exist() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("sources_and_tests_parameter_does_not_exist"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "sources_and_tests_parameter_does_not_exist") + void sources_and_tests_parameter_does_not_exist(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { // check sources FileMapperOptions sources = utPlsqlMojo.buildSourcesOptions(); assertEquals(0, sources.getFilePaths().size()); @@ -128,10 +130,8 @@ public void sources_and_tests_parameter_does_not_exist() throws Exception { * Then : it should find all sources/tests files in default directories */ @Test - public void sources_and_tests_parameter_does_not_exist_but_default_directory_exists() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("sources_and_tests_parameter_does_not_exist_but_default_directory_exists"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "sources_and_tests_parameter_does_not_exist_but_default_directory_exists") + void sources_and_tests_parameter_does_not_exist_but_default_directory_exists(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { // check sources FileMapperOptions sources = utPlsqlMojo.buildSourcesOptions(); assertEquals(2, sources.getFilePaths().size()); @@ -153,10 +153,8 @@ public void sources_and_tests_parameter_does_not_exist_but_default_directory_exi * Then : it should find all sources/tests files in default directories */ @Test - public void sources_and_tests_parameter_have_not_directory_tag() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("sources_and_tests_parameter_have_not_directory_tag"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "sources_and_tests_parameter_have_not_directory_tag") + void sources_and_tests_parameter_have_not_directory_tag(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { // check sources FileMapperOptions sources = utPlsqlMojo.buildSourcesOptions(); assertEquals(2, sources.getFilePaths().size()); @@ -179,10 +177,8 @@ public void sources_and_tests_parameter_have_not_directory_tag() throws Exceptio * Then : it should find all sources/tests files in default directories */ @Test - public void sources_and_tests_parameter_have_not_includes_tag() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("sources_and_tests_parameter_have_not_includes_tag"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "sources_and_tests_parameter_have_not_includes_tag") + void sources_and_tests_parameter_have_not_includes_tag(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { // check sources FileMapperOptions sources = utPlsqlMojo.buildSourcesOptions(); assertEquals(2, sources.getFilePaths().size()); @@ -204,10 +200,8 @@ public void sources_and_tests_parameter_have_not_includes_tag() throws Exception * Then : it should set the correct output channels */ @Test - public void default_console_output_behaviour() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("default_console_output_behaviour"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "default_console_output_behaviour") + void default_console_output_behaviour(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); // Assert that we added only the necessary reporters to the writer. @@ -235,10 +229,8 @@ public void default_console_output_behaviour() throws Exception { * Then : it should set the default reporter */ @Test - public void default_reporter() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("default_reporter"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "default_reporter") + void default_reporter(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals(1, utPlsqlMojo.reporters.size()); @@ -253,18 +245,19 @@ public void default_reporter() throws Exception { * Then : Tests are skipped */ @Test - public void skip_utplsql_tests() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("skip_utplsql_tests"); - assertNotNull(utPlsqlMojo); - - final ByteArrayOutputStream console = new ByteArrayOutputStream(); - System.setOut(new PrintStream(console)); + @Basedir(UNIT_TESTS + "skip_utplsql_tests") + void skip_utplsql_tests(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { + List infoMessages = new ArrayList<>(); + utPlsqlMojo.setLog(new SystemStreamLog() { + @Override + public void info(CharSequence content) { + infoMessages.add(content.toString()); + } + }); utPlsqlMojo.execute(); - String standardOutput = console.toString(); - - assertTrue(standardOutput.contains("utPLSQLTests are skipped.")); + assertTrue(infoMessages.contains("utPLSQLTests are skipped.")); } /** @@ -275,10 +268,8 @@ public void skip_utplsql_tests() throws Exception { * Then : Property is set */ @Test - public void ora_stuck_timeout() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("ora_stuck_timeout"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "ora_stuck_timeout") + void ora_stuck_timeout(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals(5, (int) utPlsqlMojo.oraStuckTimeout); @@ -292,36 +283,11 @@ public void ora_stuck_timeout() throws Exception { * Then : DBMS_OUTPUT is enabled */ @Test - public void dbms_output() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("dbms_output"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "dbms_output") + void dbms_output(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); } - /** - * DB configuration from System Properties - *

- * Given : a pom.xml without dbUrl, dbUser and dbPass configured - * When : pom is read - * Then : System Properties must be used to configure database - */ - @Test - public void db_config_using_system_properties() throws Exception { - System.setProperty("dbUrl", "jdbc:oracle:thin:@//localhost:1521/FREEPDB1"); - System.setProperty("dbUser", "UT3"); - System.setProperty("dbPass", "ut3"); - - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("db_config_using_system_properties"); - assertNotNull(utPlsqlMojo); - - utPlsqlMojo.execute(); - - System.clearProperty("dbUrl"); - System.clearProperty("dbUser"); - System.clearProperty("dbPass"); - } - /** * Exclude a list of objects *

@@ -330,10 +296,8 @@ public void db_config_using_system_properties() throws Exception { * Then : Objects are excluded */ @Test - public void exclude_object() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("exclude_object"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "exclude_object") + void exclude_object(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("app.pkg_test_me,app.test_pkg_test_me", utPlsqlMojo.excludeObject); @@ -347,10 +311,8 @@ public void exclude_object() throws Exception { * Then : Objects are included */ @Test - public void include_object() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("include_object"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "include_object") + void include_object(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("app.pkg_test_me,app.test_pkg_test_me", utPlsqlMojo.includeObject); @@ -364,10 +326,8 @@ public void include_object() throws Exception { * Then : Objects are included */ @Test - public void include_object_expr() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("include_object_expr"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "include_object_expr") + void include_object_expr(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("APP.*", utPlsqlMojo.includeObjectExpr); @@ -381,10 +341,8 @@ public void include_object_expr() throws Exception { * Then : Objects are included */ @Test - public void exclude_object_expr() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("exclude_object_expr"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "exclude_object_expr") + void exclude_object_expr(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("*", utPlsqlMojo.excludeObjectExpr); @@ -399,10 +357,8 @@ public void exclude_object_expr() throws Exception { * Then : Objects are included */ @Test - public void include_schema_expr() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("include_schema_expr"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "include_schema_expr") + void include_schema_expr(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("APP", utPlsqlMojo.includeSchemaExpr); @@ -416,17 +372,10 @@ public void include_schema_expr() throws Exception { * Then : Objects are included */ @Test - public void exclude_schema_expr() throws Exception { - UtPlsqlMojo utPlsqlMojo = createUtPlsqlMojo("exclude_schema_expr"); - assertNotNull(utPlsqlMojo); - + @Basedir(UNIT_TESTS + "exclude_schema_expr") + void exclude_schema_expr(@InjectMojo(goal = "test", pom = "pom.xml") UtPlsqlMojo utPlsqlMojo) throws Exception { utPlsqlMojo.execute(); assertEquals("*", utPlsqlMojo.excludeSchemaExpr); } - - private UtPlsqlMojo createUtPlsqlMojo(String directory) throws Exception { - return (UtPlsqlMojo) rule.lookupConfiguredMojo(new File("src/test/resources/unit-tests/" + directory), "test"); - } - } diff --git a/src/test/resources/unit-tests/wallet_connection/pom.xml b/src/test/resources/unit-tests/wallet_connection/pom.xml new file mode 100644 index 0000000..5dd0c84 --- /dev/null +++ b/src/test/resources/unit-tests/wallet_connection/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + org.utplsql + utplsql-maven-plugin-test + 3.1.0-SNAPSHOT + pom + + + + + ../../../target + + + org.utplsql + utplsql-maven-plugin + @project.version@ + + test + + + + true + + app + + + + + +