From 7e410ea5bb88893f7fff56989b2e9879a5a8cfc6 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Fri, 3 Nov 2023 14:37:25 +0100 Subject: [PATCH] Use fixed thread pool --- .gitignore | 2 +- CONTRIBUTING.md | 8 +- README.md | 39 ++- checkstyle.xml | 4 +- .../java/tracking/DaemonThreadFactory.java | 26 -- .../matomo/java/tracking/MatomoRequest.java | 4 +- .../matomo/java/tracking/MatomoTracker.java | 24 +- .../java/tracking/TrackerConfiguration.java | 9 + .../tracking/parameters/AcceptLanguage.java | 2 +- .../matomo/java/tracking/parameters/Hex.java | 4 +- .../java/tracking/parameters/VisitorId.java | 26 +- .../tracking/DaemonThreadFactoryTest.java | 36 --- .../matomo/java/tracking/MatomoTrackerIT.java | 231 ++++++++++-------- .../java/tracking/PiwikRequestTest.java | 26 +- .../matomo/java/tracking/PiwikTrackerIT.java | 77 +++--- .../java/tracking/parameters/HexTest.java | 37 +++ .../tracking/parameters/VisitorIdTest.java | 9 + 17 files changed, 283 insertions(+), 281 deletions(-) delete mode 100644 src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java delete mode 100644 src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java create mode 100644 src/test/java/org/matomo/java/tracking/parameters/HexTest.java diff --git a/.gitignore b/.gitignore index 74c79135..8a167ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ info # Package Files # **/target/ -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # IDEA files diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c208038..7dfdf721 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Please note we have a code of conduct, please follow it in all your interactions 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). + Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org/). 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. @@ -86,8 +86,8 @@ members of the project's leadership. ### Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org +[homepage]: https://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 7c59ce12..cee8de59 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Matomo Java Tracker +# Official Matomo Java Tracker [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) [![Build Status](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml/badge.svg)](https://github.com/matomo-org/matomo-java-tracker/actions/workflows/build.yml) @@ -17,7 +17,7 @@ Features include: * Includes tracking parameters for campaigns, events, downloads, outlinks, site search, devices, visiors * Supports Java 8 and higher * Allows you to skip SSL certificate validation (not recommended for production) -* Contains nearly no dependencies +* Contains nearly no runtime dependencies (only SLF4J) * Allows asynchronous requests * Supports Matomo 4 and 5 * Single and multiple requests can be sent @@ -161,7 +161,7 @@ public class YourImplementation { MatomoRequest request = MatomoRequest .builder() .siteId(42) - .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionUrl("https://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url .actionName("LearnMore") .build(); } @@ -229,7 +229,8 @@ The Matomo Tracker currently supports the following builder methods: port and username must be set as well. * `.disableSslCertValidation(...)` If set to true, the SSL certificate of the Matomo server will not be validated. This should only be used for testing purposes. Default: false -* `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This should +* `.disableSslHostVerification(...)` If set to true, the SSL host of the Matomo server will not be validated. This + should only be used for testing purposes. Default: false To send a single request synchronously via GET, call @@ -262,9 +263,7 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendRequestAsync(request); - // execute the request: - future.get(); + tracker.sendRequestAsync(request); } @@ -277,8 +276,6 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java package example; -import java.util.concurrent.CompletableFuture; -import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; @@ -287,7 +284,6 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class YourImplementation { @@ -303,9 +299,7 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendBulkRequestAsync(requests); - // execute the request - future.get(); + tracker.sendBulkRequestAsync(requests); } @@ -319,7 +313,6 @@ the bulk request through ```java package example; -import java.util.concurrent.CompletableFuture; import org.apache.http.HttpResponse; import org.matomo.java.tracking.MatomoLocale; import org.matomo.java.tracking.MatomoRequest; @@ -331,7 +324,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Locale; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class YourImplementation { @@ -351,12 +343,8 @@ public class YourImplementation { .apiEndpoint(URI.create("https://your-matomo-domain.tld/matomo.php")) .build()); - CompletableFuture future = tracker.sendBulkRequestAsync( - requests, - "33dc3f2536d3025974cccb4b4d2d98f4" - ); // second parameter is authentication token need for country override - // execute the request: - future.get(); + // second parameter is authentication token need for country override + tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); } @@ -465,7 +453,7 @@ version can be used in your local Maven repository for testing purposes, e.g. ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see +We use [SemVer](https://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/matomo-org/matomo-java-tracker/tags). ## Contribute @@ -490,6 +478,13 @@ process for submitting pull requests to us. We use Checkstyle and JaCoCo to ensure code quality. Please run `mvn verify` before submitting a pull request. Please provide tests for your changes. We use JUnit 5 for testing. Coverage should be at least 80%. +## Other Java Matomo Tracker Implementations + +* [Matomo SDK for Android](https://github.com/matomo-org/matomo-sdk-android) +* [piwik-tracking](https://github.com/ralscha/piwik-tracking) +* [Matomo Tracking API Java Client](https://github.com/dheid/matomo-tracker) -> Most of the code was integrated in the + official Matomo Java Tracker + ## License This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). diff --git a/checkstyle.xml b/checkstyle.xml index 2e632c85..2345c267 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -8,7 +8,7 @@ that can be found at https://google.github.io/styleguide/javaguide.html Checkstyle is very configurable. Be sure to read the documentation at - http://checkstyle.org (or in your downloaded distribution). + https://checkstyle.org (or in your downloaded distribution). To completely disable a check, just comment it out or delete it from the file. To suppress certain violations please review suppression filters. @@ -35,7 +35,7 @@ - + diff --git a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java b/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java deleted file mode 100644 index 35752f6f..00000000 --- a/src/main/java/org/matomo/java/tracking/DaemonThreadFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Matomo Java Tracker - * - * @link https://github.com/matomo/matomo-java-tracker - * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause - */ - -package org.matomo.java.tracking; - -import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.concurrent.ThreadFactory; - -class DaemonThreadFactory implements ThreadFactory { - - @Override - public Thread newThread( - @Nullable - Runnable runnable - ) { - Thread thread = new Thread(runnable); - thread.setDaemon(true); - thread.setName("MatomoJavaTracker"); - return thread; - } - -} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 3a5e5fb6..99395a9c 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -141,7 +141,7 @@ public class MatomoRequest { /** * The campaign keyword (see - * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a + * Tracking Campaigns). Used to populate the Referrers > Campaigns report (clicking on a * campaign loads all keywords for this campaign). This parameter will only be used for the first pageview of a visit. */ @TrackingParameter(name = "_rck") @@ -841,7 +841,7 @@ public MatomoDate getRequestDatetime() { * Set the datetime of the request (normally the current time is used). * This can be used to record visits and page views in the past. The datetime * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Matomo to re-process + * need to force Matomo to re-process * reports for the past dates. If you set the Request Datetime to a datetime * older than four hours then Auth Token must be set. If you set * Request Datetime with a datetime in the last four hours then you diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 98c26877..8ee5580a 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -13,7 +13,7 @@ import java.net.URI; import java.time.Duration; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.Executors; import java.util.function.Consumer; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @@ -35,7 +35,7 @@ public class MatomoTracker { * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. Must not be null + * https://your-matomo-domain.tld/matomo.php. Must not be null * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @Deprecated @@ -51,7 +51,7 @@ public MatomoTracker( * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param timeout the timeout of the sent request in milliseconds or -1 if not set * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} */ @@ -68,7 +68,7 @@ public MatomoTracker( * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param proxyHost The hostname or IP address of an optional HTTP proxy, null allowed * @param proxyPort The port of an HTTP proxy or -1 if not set * @param timeout the timeout of the request in milliseconds or -1 if not set @@ -104,20 +104,10 @@ public MatomoTracker( requireNonNull(trackerConfiguration, "Tracker configuration must not be null"); trackerConfiguration.validate(); this.trackerConfiguration = trackerConfiguration; - ScheduledThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor(); sender = new Sender(trackerConfiguration, new QueryCreator(trackerConfiguration), - threadPoolExecutor - ); - } - - @edu.umd.cs.findbugs.annotations.NonNull - private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { - DaemonThreadFactory threadFactory = new DaemonThreadFactory(); - ScheduledThreadPoolExecutor threadPoolExecutor = - new ScheduledThreadPoolExecutor(1, threadFactory); - threadPoolExecutor.setRemoveOnCancelPolicy(true); - return threadPoolExecutor; + Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize() + )); } /** @@ -125,7 +115,7 @@ private static ScheduledThreadPoolExecutor createThreadPoolExecutor() { * Tracking HTTP API endpoint via the provided proxy. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * https://your-matomo-domain.tld/matomo.php. * @param proxyHost url endpoint for the proxy, null allowed * @param proxyPort proxy server port number or -1 if not set * @deprecated Please use {@link MatomoTracker#MatomoTracker(TrackerConfiguration)} diff --git a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java index 81a89c33..1376cf42 100644 --- a/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java +++ b/src/main/java/org/matomo/java/tracking/TrackerConfiguration.java @@ -123,6 +123,15 @@ public class TrackerConfiguration { */ boolean disableSslHostVerification; + /** + * The thread pool size for the async sender. Defaults to 2. + * + *

Attention: If you use this library in a web application, make sure that this thread pool + * does not exceed the thread pool of the web application. Otherwise, you might run into + * problems. + */ + int threadPoolSize = 2; + /** * Validates the auth token. The auth token must be exactly 32 characters long. */ diff --git a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java index 934ec85f..0ecee82d 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java +++ b/src/main/java/org/matomo/java/tracking/parameters/AcceptLanguage.java @@ -19,7 +19,7 @@ /** * Describes the content for the Accept-Language header field that can be overridden by a custom parameter. The format - * is specified in the corresponding RFC 4647 Matching of Language Tags + * is specified in the corresponding RFC 4647 Matching of Language Tags * *

Example: "en-US,en;q=0.8,de;q=0.6" */ diff --git a/src/main/java/org/matomo/java/tracking/parameters/Hex.java b/src/main/java/org/matomo/java/tracking/parameters/Hex.java index 3387afe5..13ab4d98 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/Hex.java +++ b/src/main/java/org/matomo/java/tracking/parameters/Hex.java @@ -7,13 +7,15 @@ package org.matomo.java.tracking.parameters; +import lombok.NonNull; + final class Hex { private Hex() { // utility class } - static String fromBytes(byte[] bytes) { + static String fromBytes(@NonNull byte[] bytes) { StringBuilder result = new StringBuilder(bytes.length * 2); for (byte b : bytes) { result.append(String.format("%02x", b)); diff --git a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java index fcc4e64f..cbf0404d 100644 --- a/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java +++ b/src/main/java/org/matomo/java/tracking/parameters/VisitorId.java @@ -7,6 +7,7 @@ package org.matomo.java.tracking.parameters; +import edu.umd.cs.findbugs.annotations.Nullable; import java.security.SecureRandom; import java.util.Random; import java.util.UUID; @@ -33,6 +34,7 @@ public class VisitorId { * * @return A randomly generated visitor id */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId random() { VisitorId visitorId = new VisitorId(); RANDOM.nextBytes(visitorId.representation); @@ -48,6 +50,7 @@ public static VisitorId random() { * @param hash A number (e.g. a hash code) to create the visitor id from * @return Always the same visitor id for the same input */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId fromHash(long hash) { VisitorId visitorId = new VisitorId(); long remainingHash = hash; @@ -66,6 +69,7 @@ public static VisitorId fromHash(long hash) { * @param uuid A UUID to create the visitor id from * @return The visitor id for the given UUID */ + @edu.umd.cs.findbugs.annotations.NonNull public static VisitorId fromUUID(@NonNull UUID uuid) { return fromHash(uuid.getMostSignificantBits()); } @@ -79,8 +83,9 @@ public static VisitorId fromUUID(@NonNull UUID uuid) { * @param inputHex A hexadecimal string to create the visitor id from * @return The visitor id for the given input */ - public static VisitorId fromHex(String inputHex) { - if (inputHex == null || inputHex.trim().isEmpty()) { + @edu.umd.cs.findbugs.annotations.NonNull + public static VisitorId fromHex(@NonNull String inputHex) { + if (inputHex.trim().isEmpty()) { throw new IllegalArgumentException("Hex string must not be null or empty"); } if (inputHex.length() > 16) { @@ -101,12 +106,25 @@ public static VisitorId fromHex(String inputHex) { throw new IllegalArgumentException("Input must be a valid hex string", e); } } - - return visitorId; } + /** + * Creates a visitor id from a string. The string will be hashed to create the visitor id. + * + * @param str A string to create the visitor id from + * @return The visitor id for the given string or null if the string is null or empty + */ + @Nullable + public static VisitorId fromString(@Nullable String str) { + if (str == null || str.trim().isEmpty()) { + return null; + } + return fromHash(str.hashCode()); + } + @Override + @edu.umd.cs.findbugs.annotations.NonNull public String toString() { return Hex.fromBytes(representation); } diff --git a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java b/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java deleted file mode 100644 index eb569c9b..00000000 --- a/src/test/java/org/matomo/java/tracking/DaemonThreadFactoryTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.matomo.java.tracking; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.concurrent.ThreadFactory; -import org.junit.jupiter.api.Test; - -class DaemonThreadFactoryTest { - - private final ThreadFactory daemonThreadFactory = new DaemonThreadFactory(); - - private Thread thread; - - @Test - void threadIsDaemonThread() { - - whenCreatesThread(); - - assertThat(thread.isDaemon()).isTrue(); - - } - - private void whenCreatesThread() { - thread = daemonThreadFactory.newThread(null); - } - - @Test - void threadHasName() { - - whenCreatesThread(); - - assertThat(thread.getName()).isEqualTo("MatomoJavaTracker"); - - } - -} diff --git a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 0f869f93..7adb35b8 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -10,6 +10,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singleton; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -21,6 +22,7 @@ import java.util.Arrays; import java.util.Locale.LanguageRange; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -55,6 +57,8 @@ class MatomoTrackerIT { private CompletableFuture future; + private MatomoTracker matomoTracker; + @BeforeAll static void beforeAll() { wireMockServer.start(); @@ -79,7 +83,9 @@ void requiresApiEndpoint() { @Test void requiresSiteId() { - trackerConfigurationBuilder.apiEndpoint(URI.create("http://localhost:8099/matomo.php")).build(); + trackerConfigurationBuilder + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) + .build(); assertThatThrownBy(this::whenSendsRequestAsync) .isInstanceOf(IllegalArgumentException.class) @@ -88,13 +94,8 @@ void requiresSiteId() { } private void whenSendsRequestAsync() { - future = - new MatomoTracker(trackerConfigurationBuilder.build()).sendRequestAsync(requestBuilder.build()); - try { - future.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); + future = matomoTracker.sendRequestAsync(requestBuilder.build()); } @Test @@ -110,19 +111,16 @@ void usesDefaultSiteId() { private void givenTrackerConfigurationWithDefaultSiteId() { trackerConfigurationBuilder - .apiEndpoint(URI.create(String.format( - "http://localhost:%s/matomo.php", - wireMockServer.port() - ))) + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/matomo.php")) .defaultSiteId(SITE_ID); } private void thenGetsRequest(String expectedQuery) { - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo(String.format( - "/matomo.php?%s", - expectedQuery - ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo(String.format("/matomo.php?%s", + expectedQuery + ))).withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); } @Test @@ -143,9 +141,14 @@ void validatesTokenAuth() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.authToken("invalid-token-auth"); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Auth token must be exactly 32 characters long"); + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Auth token must be exactly 32 characters long"); } @@ -218,21 +221,18 @@ void encodesLink() { private void whenSendsBulkRequestAsync() { future = new MatomoTracker(trackerConfigurationBuilder.build()).sendBulkRequestAsync(singleton( requestBuilder.build())); - try { - future.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } } private void thenPostsRequestWithoutAuthToken(String expectedQuery, String contentLength) { - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo(contentLength)) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo(contentLength)) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + expectedQuery + "\"]}"))); + }); + } @Test @@ -257,11 +257,11 @@ void getContainsHeaders() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); } @@ -272,12 +272,13 @@ void postContainsHeaders() { whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Length", equalTo("90")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Length", equalTo("90")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient"))); + }); } @@ -289,11 +290,11 @@ void allowsToOverrideUserAgent() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader( - "User-Agent", - equalTo("Mozilla/5.0") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php")).withHeader("User-Agent", + equalTo("Mozilla/5.0") + )); + }); } @@ -339,28 +340,30 @@ void tracksMinimalRequest() { whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("711")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson("{\"requests\":[\"?" - + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" - + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("711")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson("{\"requests\":[\"?" + + "idsite=42&rec=1&action_name=Help+%2F+Feedback&url=https%3A%2F%2Fwww.daniel-heid.de%2Fportfolio&apiv=1&_id=2fa93d2858bc4867&urlref=https%3A%2F%2Fwww.daniel-heid.de%2Freferrer&_cvar=%7B%224%22%3A%5B%22customVariable1Key%22%2C%22customVariable1Value%22%5D%2C%225%22%3A%5B%22customVariable2Key%22%2C%22customVariable2Value%22%5D%7D&_idvc=2&_idts=1660070052&res=1024x768&lang=de%2Cde-de%3Bq%3D0.9%2Cen%3Bq%3D0.8&pv_id=lbBbxG&idgoal=0&revenue=12.34&ec_items=%5B%5B%22SKU%22%2C%22%22%2C%22%22%2C0.0%2C0%5D%2C%5B%22SKU%22%2C%22NAME%22%2C%22CATEGORY%22%2C123.4%2C0%5D%5D&token_auth=fdf6e8461ea9de33176b222519627f78&country=de&send_image=0&rand=someRandom" + + "\"],\"token_auth\" : \"" + "fdf6e8461ea9de33176b222519627f78" + "\"}"))); + + }); } @Test - void doesNothingIfNotEnabled() { + void doesNothingIfNotEnabled() throws Exception { wireMockServer.resetRequests(); givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsRequestAsync(); + future.get(); - assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); } @@ -375,20 +378,24 @@ void exampleWorks() { .build(); // Prepare the tracker (stateless - can be used for multiple actions) - MatomoTracker tracker = new MatomoTracker(config); + MatomoTracker matomoTracker = new MatomoTracker(config); - // Track an action - CompletableFuture future = tracker.sendRequestAsync(MatomoRequest + // Track an action asynchronuously + CompletableFuture future = matomoTracker.sendRequestAsync(MatomoRequest .builder() .actionName("User Profile / Upload Profile Picture") .actionUrl("https://your-domain.net/user/profile/picture") - .visitorId(VisitorId.fromHash("some@email-adress.org".hashCode())) + .visitorId(VisitorId.fromString("some@email-adress.org")) // ... .build()); // If you want to ensure the request has been handled: - if (future.isCompletedExceptionally()) { - // log, throw, ... + try { + future.get(); + } catch (InterruptedException e) { + // Occurs if the current thread is interrupted while waiting + } catch (ExecutionException e) { + // Happens on any exception during the request } } @@ -397,16 +404,17 @@ void reportsErrors() { wireMockServer.stubFor(get(urlPathEqualTo("/failing")).willReturn(status(500))); trackerConfigurationBuilder - .apiEndpoint(URI.create(String.format("http://localhost:%d/failing", - wireMockServer.port() - ))) + .apiEndpoint(URI.create(wireMockServer.baseUrl() + "/failing")) .defaultSiteId(SITE_ID); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(MatomoException.class) - .hasRootCauseMessage("Tracking endpoint responded with code 500"); + whenSendsRequestAsync(); - assertThat(future).isCompletedExceptionally(); + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(MatomoException.class) + .withMessage("Tracking endpoint responded with code 500"); } @@ -418,12 +426,12 @@ void includesDefaultTokenAuth() { whenSendsRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?idsite=42token_auth=fdf6e8461ea9de33176b222519627f78&rec=1&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom")).withHeader("User-Agent", + equalTo("MatomoJavaClient") + )); + }); } @@ -431,23 +439,23 @@ void includesDefaultTokenAuth() { void includesMultipleQueriesInBulkRequest() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); - CompletableFuture future1 = - tracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), + future = + matomoTracker.sendBulkRequestAsync(Arrays.asList(requestBuilder.actionName("First").build(), requestBuilder.actionName("Second").build(), requestBuilder.actionName("Third").build() )); - future1.get(); - assertThat(future1).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("297")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("297")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?idsite=42&rec=1&action_name=First&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Second&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\", \"?idsite=42&rec=1&action_name=Third&apiv=1&_id=00bbccddeeff1122&send_image=0&rand=someRandom\" ]}"))); + }); } @@ -457,19 +465,26 @@ void failsOnNegativeSiteId() { givenTrackerConfigurationWithDefaultSiteId(); requestBuilder.siteId(-1); - assertThatThrownBy(this::whenSendsRequestAsync) - .hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Site ID must not be negative"); + whenSendsRequestAsync(); + + assertThat(future) + .failsWithin(1, MINUTES) + .withThrowableThat() + .havingRootCause() + .isInstanceOf(IllegalArgumentException.class) + .withMessage("Site ID must not be negative"); + + ; } @Test - void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() { + void doesNotSendRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsRequestAsync(); + future.get(); - assertThat(future).isNotCompletedExceptionally(); wireMockServer.verify(0, getRequestedFor(urlPathEqualTo("/matomo.php"))); } @@ -503,42 +518,42 @@ private void whenSendsBulkRequest() { } @Test - void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() { + void doesNotSendBulkRequestAsyncIfTrackerConfigurationIsDisabled() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); trackerConfigurationBuilder.enabled(false); whenSendsBulkRequestAsync(); - assertThat(future).isNotCompletedExceptionally(); + future.get(); wireMockServer.verify(0, postRequestedFor(urlPathEqualTo("/matomo.php"))); } @Test void sendsRequestAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - CompletableFuture future = tracker.sendRequestAsync(requestBuilder.build(), v -> { + future = matomoTracker.sendRequestAsync(requestBuilder.build(), v -> { success.set(true); }); - future.get(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); + }); assertThat(success).isTrue(); } @Test void sendsRequestsAsyncAndAcceptsCallback() throws Exception { givenTrackerConfigurationWithDefaultSiteId(); - MatomoTracker tracker = new MatomoTracker(trackerConfigurationBuilder.build()); + matomoTracker = new MatomoTracker(trackerConfigurationBuilder.build()); AtomicBoolean success = new AtomicBoolean(); - CompletableFuture future = - tracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { + future = + matomoTracker.sendBulkRequestAsync(singleton(requestBuilder.build()), v -> { success.set(true); }); - future.get(); - assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/matomo.php"))); + }); assertThat(success).isTrue(); } diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 04693cf9..49432619 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -28,15 +28,15 @@ class PiwikRequestTest { @BeforeEach void setUp() throws Exception { - request = new PiwikRequest(3, new URL("http://test.com")); + request = new PiwikRequest(3, new URL("https://test.com")); } @Test void testConstructor() throws Exception { - request = new PiwikRequest(3, new URL("http://test.com")); + request = new PiwikRequest(3, new URL("https://test.com")); assertThat(request.getSiteId()).isEqualTo(Integer.valueOf(3)); assertThat(request.getRequired()).isTrue(); - assertThat(request.getActionUrl()).isEqualTo("http://test.com"); + assertThat(request.getActionUrl()).isEqualTo("https://test.com"); assertThat(request.getVisitorId()).isNotNull(); assertThat(request.getRandomValue()).isNotNull(); assertThat(request.getApiVersion()).isEqualTo("1"); @@ -61,8 +61,8 @@ void testActionName() { void testActionUrl() { request.setActionUrl(null); assertThat(request.getActionUrl()).isNull(); - request.setActionUrl("http://action.com"); - assertThat(request.getActionUrl()).isEqualTo("http://action.com"); + request.setActionUrl("https://action.com"); + assertThat(request.getActionUrl()).isEqualTo("https://action.com"); } /** @@ -147,8 +147,8 @@ void testContentPiece() { */ @Test void testContentTarget() { - request.setContentTarget("http://target.com"); - assertThat(request.getContentTarget()).isEqualTo("http://target.com"); + request.setContentTarget("https://target.com"); + assertThat(request.getContentTarget()).isEqualTo("https://target.com"); } /** @@ -292,8 +292,8 @@ void testDeviceResolution() { @Test void testDownloadUrl() { - request.setDownloadUrl("http://download.com"); - assertThat(request.getDownloadUrl()).isEqualTo("http://download.com"); + request.setDownloadUrl("https://download.com"); + assertThat(request.getDownloadUrl()).isEqualTo("https://download.com"); } /** @@ -537,8 +537,8 @@ void testNewVisit() { */ @Test void testOutlinkUrl() { - request.setOutlinkUrl("http://outlink.com"); - assertThat(request.getOutlinkUrl()).isEqualTo("http://outlink.com"); + request.setOutlinkUrl("https://outlink.com"); + assertThat(request.getOutlinkUrl()).isEqualTo("https://outlink.com"); } /** @@ -661,8 +661,8 @@ void testRandomValue() { */ @Test void testReferrerUrl() { - request.setReferrerUrl("http://referrer.com"); - assertThat(request.getReferrerUrl()).isEqualTo("http://referrer.com"); + request.setReferrerUrl("https://referrer.com"); + assertThat(request.getReferrerUrl()).isEqualTo("https://referrer.com"); } /** diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java index 6f61f737..1df7512d 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerIT.java @@ -9,6 +9,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.status; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -46,10 +47,7 @@ static void beforeAll() { @BeforeEach void setUp() throws MalformedURLException { - piwikTracker = new PiwikTracker( - String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - -1 - ); + piwikTracker = new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", -1); wireMockServer.resetRequests(); wireMockServer.stubFor(post(urlPathEqualTo("/matomo.php")).willReturn(status(204))); wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); @@ -68,8 +66,7 @@ void testSendRequest() { piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( - "User-Agent", + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader("User-Agent", equalTo("MatomoJavaClient") )); } @@ -82,14 +79,15 @@ void testSendRequestAsync() throws Exception { request.setCustomTrackingParameter("parameterName", "parameterValue"); CompletableFuture future = piwikTracker.sendRequestAsync(request); - future.get(); assertThat(future).isNotCompletedExceptionally(); - wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")).withHeader( - "User-Agent", - equalTo("MatomoJavaClient") - )); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(getRequestedFor(urlEqualTo( + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue")) + .withHeader("User-Agent", equalTo("MatomoJavaClient") + )); + }); + } @@ -169,17 +167,17 @@ void testSendBulkRequestAsync_Iterable() throws Exception { request.setCustomTrackingParameter("parameterName", "parameterValue"); CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests); - future.get(); - assertThat(future).isNotCompletedExceptionally(); + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("167")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("167")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\" : [ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\" ]}"))); + }); } @@ -207,30 +205,28 @@ void testSendBulkRequestAsync_Iterable_String() throws Exception { CompletableFuture future = piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012"); - future.get(); assertThat(future).isNotCompletedExceptionally(); - - wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) - .withHeader("Content-Length", equalTo("215")) - .withHeader("Accept", equalTo("*/*")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("User-Agent", equalTo("MatomoJavaClient")) - .withRequestBody(equalToJson( - "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); - + assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { + wireMockServer.verify(postRequestedFor(urlEqualTo("/matomo.php")) + .withHeader("Content-Length", equalTo("215")) + .withHeader("Accept", equalTo("*/*")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("User-Agent", equalTo("MatomoJavaClient")) + .withRequestBody(equalToJson( + "{\"requests\":[ \"?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand¶meterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + + }); } @Test void createsPiwikTrackerWithHostUrl() { - PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port())); + PiwikTracker piwikTracker = new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php"); piwikTracker.sendRequest(request); wireMockServer.verify(getRequestedFor(urlEqualTo( - "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader( - "User-Agent", + "/matomo.php?rec=1&idsite=42&url=https%3A%2F%2Ftest.local%2Ftest%2Fpath%3Fid%3D123&apiv=1&_id=0de0b6b3a763ffff&send_image=0&rand=rand")).withHeader("User-Agent", equalTo("MatomoJavaClient") )); } @@ -238,10 +234,7 @@ void createsPiwikTrackerWithHostUrl() { @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - "localhost", - 8080 - ); + new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", "localhost", 8080); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) @@ -252,11 +245,7 @@ void createPiwikTrackerWithHostUrlAndProxyHostAndPort() { @Test void createPiwikTrackerWithHostUrlAndProxyHostAndPortAndTimeout() { PiwikTracker piwikTracker = - new PiwikTracker(String.format("http://localhost:%d/matomo.php", wireMockServer.port()), - "localhost", - 8080, - 1000 - ); + new PiwikTracker(wireMockServer.baseUrl() + "/matomo.php", "localhost", 8080, 1000); assertThatThrownBy(() -> piwikTracker.sendRequest(request)) .isInstanceOf(MatomoException.class) diff --git a/src/test/java/org/matomo/java/tracking/parameters/HexTest.java b/src/test/java/org/matomo/java/tracking/parameters/HexTest.java new file mode 100644 index 00000000..5f07f27d --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/parameters/HexTest.java @@ -0,0 +1,37 @@ +package org.matomo.java.tracking.parameters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HexTest { + + + + @Test + void failsIfBytesAreNull() { + assertThatThrownBy(() -> Hex.fromBytes(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("bytes is marked non-null but is null"); + } + + private static Stream testBytes() { + return Stream.of( + Arguments.of(new byte[] {0x00, 0x01, 0x02, 0x03}, "00010203"), + Arguments.of(new byte[] {(byte) 0xFF, (byte) 0xFE, (byte) 0xFD, (byte) 0xFC}, "fffefdfc"), + Arguments.of(new byte[0], "") + ); + } + + @ParameterizedTest + @MethodSource("testBytes") + void convertsBytesIntoHex(byte[] bytes, String expected) { + assertThat(Hex.fromBytes(bytes)).isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java index aff51fbf..45aa34de 100644 --- a/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java +++ b/src/test/java/org/matomo/java/tracking/parameters/VisitorIdTest.java @@ -177,4 +177,13 @@ void failsOnNullUUID() { .hasMessage("uuid is marked non-null but is null"); } + @Test + void createsVisitorIdFromString() { + + VisitorId visitorId = VisitorId.fromString("test"); + + assertThat(visitorId).hasToString("0000000000364492"); + + } + }