diff --git a/src/main/java/com/saucelabs/saucerest/api/InsightsEndpoint.java b/src/main/java/com/saucelabs/saucerest/api/InsightsEndpoint.java index c93722fe..2b52a4ab 100644 --- a/src/main/java/com/saucelabs/saucerest/api/InsightsEndpoint.java +++ b/src/main/java/com/saucelabs/saucerest/api/InsightsEndpoint.java @@ -2,13 +2,29 @@ import com.saucelabs.saucerest.DataCenter; import com.saucelabs.saucerest.HttpMethod; +import com.saucelabs.saucerest.JobSource; import com.saucelabs.saucerest.Unfinished; +import com.saucelabs.saucerest.model.insights.AnalyticsTrendsParameter; +import com.saucelabs.saucerest.model.insights.Errors; +import com.saucelabs.saucerest.model.insights.ErrorsParameter; +import com.saucelabs.saucerest.model.insights.TestCases; +import com.saucelabs.saucerest.model.insights.TestCasesParameter; +import com.saucelabs.saucerest.model.insights.TestCasesStats; +import com.saucelabs.saucerest.model.insights.TestCasesStatsParameter; +import com.saucelabs.saucerest.model.insights.TestMetrics; +import com.saucelabs.saucerest.model.insights.TestMetricsParameter; import com.saucelabs.saucerest.model.insights.TestResult; import com.saucelabs.saucerest.model.insights.TestResultParameter; +import com.saucelabs.saucerest.model.insights.Tests; +import com.saucelabs.saucerest.model.insights.TestsParameter; +import com.saucelabs.saucerest.model.insights.TestTrends; +import com.saucelabs.saucerest.model.insights.TrendsErrors; +import com.saucelabs.saucerest.model.insights.TrendsErrorsParameter; +import com.saucelabs.saucerest.model.insights.TrendsTestsParameter; import java.io.IOException; -import java.util.Map; -@Unfinished("This endpoint is not yet completely implemented") +@Unfinished("This endpoint does not yet cover the Filters, Activity, Concurrency, and Coverage Insights APIs, " + + "nor the cross-source (all sources) variants of the Test Cases, Errors, and Trends APIs") public class InsightsEndpoint extends AbstractEndpoint { public InsightsEndpoint(DataCenter dataCenter) { super(dataCenter); @@ -26,11 +42,137 @@ public InsightsEndpoint(String username, String accessKey, String apiServer) { super(username, accessKey, apiServer); } + /** + * Retrieves run data for all tests matching the specified criteria. + * + * @param parameter A {@link TestResultParameter} object containing the parameters to filter the results + * @return A {@link TestResult} object + * @throws IOException when the request fails + */ public TestResult getTestResults(TestResultParameter parameter) throws IOException { String url = getBaseEndpoint() + "v1/analytics/tests"; - Map params; - params = parameter.toMap(); - return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, params), TestResult.class); + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestResult.class); } -} \ No newline at end of file + + /** + * Returns aggregated metric values, such as fastest/slowest run and status breakdown, for a specific test + * across a given period. + * + * @param parameter A {@link TestMetricsParameter} object containing the parameters to filter the results + * @return A {@link TestMetrics} object + * @throws IOException when the request fails + */ + public TestMetrics getTestMetricsSummary(TestMetricsParameter parameter) throws IOException { + String url = getBaseEndpoint() + "v1/analytics/insights/test-metrics"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestMetrics.class); + } + + /** + * Returns time-bucketed data representing the tests executed in the requested interval. + * + * @param parameter A {@link AnalyticsTrendsParameter} object containing the parameters to filter the results + * @return A {@link TestTrends} object + * @throws IOException when the request fails + */ + public TestTrends getTestTrends(AnalyticsTrendsParameter parameter) throws IOException { + String url = getBaseEndpoint() + "v1/analytics/trends/tests"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestTrends.class); + } + + /** + * Returns an array containing details of individual test executions matching the specified criteria. + * + * @param jobSource The type of device for which you are getting tests. Valid values are: {@link JobSource} + * @param parameter A {@link TestsParameter} object containing the parameters to filter the results + * @return A {@link Tests} object + * @throws IOException when the request fails + */ + public Tests getTests(JobSource jobSource, TestsParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "tests"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), Tests.class); + } + + /** + * Returns an array of test cases, grouped by name, with statistical details such as run counts and pass/fail + * rates. + * + * @param jobSource The type of device for which you are getting test cases. Valid values are: {@link JobSource} + * @param parameter A {@link TestCasesParameter} object containing the parameters to filter the results + * @return A {@link TestCases} object + * @throws IOException when the request fails + */ + public TestCases getTestCases(JobSource jobSource, TestCasesParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "test-cases"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestCases.class); + } + + /** + * Returns a high-level statistical summary of test case consistency and reliability, such as counts of + * consistently passing, failing, or erroring test cases. + * + * @param jobSource The type of device for which you are getting test case stats. Valid values are: {@link + * JobSource} + * @param parameter A {@link TestCasesStatsParameter} object containing the parameters to filter the results + * @return A {@link TestCasesStats} object + * @throws IOException when the request fails + */ + public TestCasesStats getTestCasesStats(JobSource jobSource, TestCasesStatsParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "test-cases/stats"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestCasesStats.class); + } + + /** + * Returns an array of errors, with occurrence counts, for all tests matching the specified criteria. + * + * @param jobSource The type of device for which you are getting errors. Valid values are: {@link JobSource} + * @param parameter A {@link ErrorsParameter} object containing the parameters to filter the results + * @return A {@link Errors} object + * @throws IOException when the request fails + */ + public Errors getErrors(JobSource jobSource, ErrorsParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "errors"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), Errors.class); + } + + /** + * Returns an array of buckets with aggregations, such as the number of tests run per browser, device, OS, or + * framework, for the requested interval. + * + * @param jobSource The type of device for which you are getting trends. Valid values are: {@link JobSource} + * @param parameter A {@link TrendsTestsParameter} object containing the parameters to filter the results + * @return A {@link TestTrends} object + * @throws IOException when the request fails + */ + public TestTrends getTrendsTests(JobSource jobSource, TrendsTestsParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "trends/tests"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TestTrends.class); + } + + /** + * Returns error statistics, including the tests affected by each error, for the requested interval. + * + * @param jobSource The type of device for which you are getting error trends. Valid values are: {@link + * JobSource} + * @param parameter A {@link TrendsErrorsParameter} object containing the parameters to filter the results + * @return A {@link TrendsErrors} object + * @throws IOException when the request fails + */ + public TrendsErrors getTrendsErrors(JobSource jobSource, TrendsErrorsParameter parameter) throws IOException { + String url = getBaseEndpoint(jobSource) + "trends/errors"; + + return deserializeJSONObject(requestWithQueryParameters(url, HttpMethod.GET, parameter.toMap()), TrendsErrors.class); + } + + /** The base endpoint of the v2 Insights endpoint APIs. */ + protected String getBaseEndpoint(JobSource jobSource) { + return super.getBaseEndpoint() + "v2/insights/" + jobSource.value + "/"; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Aggs.java b/src/main/java/com/saucelabs/saucerest/model/insights/Aggs.java new file mode 100644 index 00000000..95ac7fb3 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Aggs.java @@ -0,0 +1,44 @@ +package com.saucelabs.saucerest.model.insights; + +import com.google.gson.annotations.SerializedName; +import java.util.List; + +public class Aggs { + public List browser; + + @SerializedName("browserError") + public List browserError; + + @SerializedName("browserFail") + public List browserFail; + + public List device; + + @SerializedName("deviceError") + public List deviceError; + + @SerializedName("deviceFail") + public List deviceFail; + + @SerializedName("errorMessage") + public List errorMessage; + + public List framework; + + @SerializedName("frameworkError") + public List frameworkError; + + @SerializedName("frameworkFail") + public List frameworkFail; + + public List os; + + @SerializedName("osError") + public List osError; + + @SerializedName("osFail") + public List osFail; + + public List owner; + public List status; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/AnalyticsTrendsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/AnalyticsTrendsParameter.java new file mode 100644 index 00000000..2d85bc08 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/AnalyticsTrendsParameter.java @@ -0,0 +1,150 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v1/analytics/trends/tests}. */ +public class AnalyticsTrendsParameter { + private final String start; + private final String end; + private final TimeRange timeRange; + private final Scope scope; + private final Interval interval; + private final String[] browser; + private final String[] build; + private final String[] device; + private final String[] os; + private final TestResultParameter.Status status; + private final String[] tag; + private final TagFilterMode tagFilterMode; + + private AnalyticsTrendsParameter(Builder builder) { + start = builder.start; + end = builder.end; + timeRange = builder.timeRange; + scope = builder.scope; + interval = builder.interval; + browser = builder.browser; + build = builder.build; + device = builder.device; + os = builder.os; + status = builder.status; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("time_range", timeRange == null ? null : timeRange.toString()), + new AbstractMap.SimpleEntry<>("scope", scope == null ? null : scope.value), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("status", status == null ? null : status.getValue()), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private String start; + private String end; + private TimeRange timeRange; + private Scope scope; + private Interval interval; + private String[] browser; + private String[] build; + private String[] device; + private String[] os; + private TestResultParameter.Status status; + private String[] tag; + private TagFilterMode tagFilterMode; + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setTimeRange(TimeRange val) { + timeRange = val; + return this; + } + + public Builder setScope(Scope val) { + scope = val; + return this; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setStatus(TestResultParameter.Status val) { + status = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public AnalyticsTrendsParameter build() { + boolean isTimeRangeUsed = timeRange != null; + boolean isStartEndUsed = start != null && end != null; + + if (!isTimeRangeUsed && !isStartEndUsed) { + throw new IllegalStateException("Either 'time_range' or 'start' and 'end' must be set."); + } + if (isTimeRangeUsed && isStartEndUsed) { + throw new IllegalStateException("Only one of 'time_range' or 'start' and 'end' can be set, not both."); + } + + return new AnalyticsTrendsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/DateTimeUtils.java b/src/main/java/com/saucelabs/saucerest/model/insights/DateTimeUtils.java new file mode 100644 index 00000000..6ac6e163 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/DateTimeUtils.java @@ -0,0 +1,21 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +final class DateTimeUtils { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + + private DateTimeUtils() { + } + + /** Converts a {@link LocalDateTime} in the system default time zone to a UTC API date string. */ + static String toUtcString(LocalDateTime val) { + ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(val); + LocalDateTime utcDateTime = val.minusSeconds(offset.getTotalSeconds()); + + return utcDateTime.format(FORMATTER); + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/DeviceGroup.java b/src/main/java/com/saucelabs/saucerest/model/insights/DeviceGroup.java new file mode 100644 index 00000000..8df26afd --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/DeviceGroup.java @@ -0,0 +1,13 @@ +package com.saucelabs.saucerest.model.insights; + +/** Only applicable when the {@link com.saucelabs.saucerest.JobSource} is RDC. */ +public enum DeviceGroup { + PRIVATE("private"), + PUBLIC("public"); + + public final String value; + + DeviceGroup(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/ErrorBucket.java b/src/main/java/com/saucelabs/saucerest/model/insights/ErrorBucket.java new file mode 100644 index 00000000..5e94300c --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/ErrorBucket.java @@ -0,0 +1,10 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; + +public class ErrorBucket { + public String name; + public Integer count; + public List items; + public Boolean hasMore; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Errors.java b/src/main/java/com/saucelabs/saucerest/model/insights/Errors.java new file mode 100644 index 00000000..6c8a018d --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Errors.java @@ -0,0 +1,9 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; + +public class Errors { + public List buckets; + public Integer allItemsCount; + public Integer total; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/ErrorsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/ErrorsParameter.java new file mode 100644 index 00000000..ddcd66c7 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/ErrorsParameter.java @@ -0,0 +1,149 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/errors}. */ +public class ErrorsParameter { + private final String orgId; + private final Interval interval; + private final String userId; + private final String groupId; + private final String teamId; + private final String[] build; + private final String[] os; + private final String[] device; + private final String start; + private final String end; + private final Integer limit; + private final Integer offset; + private final String[] automationBackend; + + private ErrorsParameter(Builder builder) { + orgId = builder.orgId; + interval = builder.interval; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + build = builder.build; + os = builder.os; + device = builder.device; + start = builder.start; + end = builder.end; + limit = builder.limit; + offset = builder.offset; + automationBackend = builder.automationBackend; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("limit", limit), + new AbstractMap.SimpleEntry<>("offset", offset), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private Interval interval; + private String userId; + private String groupId; + private String teamId; + private String[] build; + private String[] os; + private String[] device; + private String start; + private String end; + private Integer limit; + private Integer offset; + private String[] automationBackend; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setLimit(Integer val) { + limit = val; + return this; + } + + public Builder setOffset(Integer val) { + offset = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public ErrorsParameter build() { + return new ErrorsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Interval.java b/src/main/java/com/saucelabs/saucerest/model/insights/Interval.java new file mode 100644 index 00000000..e36cdb40 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Interval.java @@ -0,0 +1,18 @@ +package com.saucelabs.saucerest.model.insights; + +public enum Interval { + ONE_MINUTE("1m"), + FIFTEEN_MINUTES("15m"), + ONE_HOUR("1h"), + SIX_HOURS("6h"), + TWELVE_HOURS("12h"), + ONE_DAY("1d"), + SEVEN_DAYS("7d"), + THIRTY_DAYS("30d"); + + public final String value; + + Interval(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/NameCount.java b/src/main/java/com/saucelabs/saucerest/model/insights/NameCount.java new file mode 100644 index 00000000..cdfbb0ce --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/NameCount.java @@ -0,0 +1,6 @@ +package com.saucelabs.saucerest.model.insights; + +public class NameCount { + public String name; + public Integer count; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/ParameterUtils.java b/src/main/java/com/saucelabs/saucerest/model/insights/ParameterUtils.java new file mode 100644 index 00000000..22287a31 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/ParameterUtils.java @@ -0,0 +1,18 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.Arrays; +import java.util.function.Function; + +final class ParameterUtils { + private ParameterUtils() { + } + + /** Maps an array of enum constants to their query-parameter string values, or {@code null} if the input is {@code null}. */ + static String[] valuesOf(T[] values, Function mapper) { + if (values == null) { + return null; + } + + return Arrays.stream(values).map(mapper).toArray(String[]::new); + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Scope.java b/src/main/java/com/saucelabs/saucerest/model/insights/Scope.java new file mode 100644 index 00000000..463af822 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Scope.java @@ -0,0 +1,13 @@ +package com.saucelabs.saucerest.model.insights; + +public enum Scope { + ME("me"), + ORGANIZATION("organization"), + SINGLE("single"); + + public final String value; + + Scope(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Sort.java b/src/main/java/com/saucelabs/saucerest/model/insights/Sort.java new file mode 100644 index 00000000..9b0cd04e --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Sort.java @@ -0,0 +1,12 @@ +package com.saucelabs.saucerest.model.insights; + +public enum Sort { + ASC("asc"), + DESC("desc"); + + public final String value; + + Sort(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Status.java b/src/main/java/com/saucelabs/saucerest/model/insights/Status.java new file mode 100644 index 00000000..03f5bfb9 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Status.java @@ -0,0 +1,17 @@ +package com.saucelabs.saucerest.model.insights; + +/** + * Test status values accepted by the v2 Insights endpoints (tests, test-cases, errors, trends). + */ +public enum Status { + COMPLETE("complete"), + ERROR("error"), + PASSED("passed"), + FAILED("failed"); + + public final String value; + + Status(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TagFilterMode.java b/src/main/java/com/saucelabs/saucerest/model/insights/TagFilterMode.java new file mode 100644 index 00000000..c4070ff3 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TagFilterMode.java @@ -0,0 +1,12 @@ +package com.saucelabs.saucerest.model.insights; + +public enum TagFilterMode { + AND("and"), + OR("or"); + + public final String value; + + TagFilterMode(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCase.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCase.java new file mode 100644 index 00000000..1b662afd --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCase.java @@ -0,0 +1,16 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.Map; + +public class TestCase { + public String name; + public Map statuses; + public Integer totalRuns; + public Double completeRate; + public Double errorRate; + public Double failRate; + public Double passRate; + public Double avgDuration; + public Double medianDuration; + public Double totalDuration; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCases.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCases.java new file mode 100644 index 00000000..76e9380d --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCases.java @@ -0,0 +1,11 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; +import java.util.Map; + +public class TestCases { + public List testCases; + public Integer total; + public Map statuses; + public Double avgRuntime; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesParameter.java new file mode 100644 index 00000000..766ad6a9 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesParameter.java @@ -0,0 +1,212 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/test-cases}. */ +public class TestCasesParameter { + private final String orgId; + private final String userId; + private final String groupId; + private final String teamId; + private final Interval interval; + private final String start; + private final String end; + private final String[] browser; + private final String[] build; + private final String[] device; + private final String[] os; + private final Status[] status; + private final String[] tag; + private final TagFilterMode tagFilterMode; + private final Integer limit; + private final Integer offset; + private final TestCasesSortBy sortBy; + private final Sort sort; + private final String[] automationBackend; + private final DeviceGroup deviceGroup; + + private TestCasesParameter(Builder builder) { + orgId = builder.orgId; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + interval = builder.interval; + start = builder.start; + end = builder.end; + browser = builder.browser; + build = builder.build; + device = builder.device; + os = builder.os; + status = builder.status; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + limit = builder.limit; + offset = builder.offset; + sortBy = builder.sortBy; + sort = builder.sort; + automationBackend = builder.automationBackend; + deviceGroup = builder.deviceGroup; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("status", ParameterUtils.valuesOf(status, s -> s.value)), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value), + new AbstractMap.SimpleEntry<>("limit", limit), + new AbstractMap.SimpleEntry<>("offset", offset), + new AbstractMap.SimpleEntry<>("sort_by", sortBy == null ? null : sortBy.value), + new AbstractMap.SimpleEntry<>("sort", sort == null ? null : sort.value), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend), + new AbstractMap.SimpleEntry<>("device_group", deviceGroup == null ? null : deviceGroup.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private String userId; + private String groupId; + private String teamId; + private Interval interval; + private String start; + private String end; + private String[] browser; + private String[] build; + private String[] device; + private String[] os; + private Status[] status; + private String[] tag; + private TagFilterMode tagFilterMode; + private Integer limit; + private Integer offset; + private TestCasesSortBy sortBy; + private Sort sort; + private String[] automationBackend; + private DeviceGroup deviceGroup; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setStatus(Status[] val) { + status = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public Builder setLimit(Integer val) { + limit = val; + return this; + } + + public Builder setOffset(Integer val) { + offset = val; + return this; + } + + public Builder setSortBy(TestCasesSortBy val) { + sortBy = val; + return this; + } + + public Builder setSort(Sort val) { + sort = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public Builder setDeviceGroup(DeviceGroup val) { + deviceGroup = val; + return this; + } + + public TestCasesParameter build() { + return new TestCasesParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesSortBy.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesSortBy.java new file mode 100644 index 00000000..f7a43617 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesSortBy.java @@ -0,0 +1,23 @@ +package com.saucelabs.saucerest.model.insights; + +public enum TestCasesSortBy { + TOTAL_RUNS("total_runs"), + NAME("name"), + COMPLETE_COUNT("complete_count"), + ERROR_COUNT("error_count"), + FAIL_COUNT("fail_count"), + PASS_COUNT("pass_count"), + COMPLETE_RATE("complete_rate"), + ERROR_RATE("error_rate"), + FAILURE_RATE("failure_rate"), + PASS_RATE("pass_rate"), + AVG_DURATION("avg_duration"), + MEDIAN_DURATION("median_duration"), + TOTAL_DURATION("total_duration"); + + public final String value; + + TestCasesSortBy(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStats.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStats.java new file mode 100644 index 00000000..5fd674d3 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStats.java @@ -0,0 +1,10 @@ +package com.saucelabs.saucerest.model.insights; + +public class TestCasesStats { + public Integer consistentlyComplete; + public Integer consistentlyError; + public Integer consistentlyFailing; + public Integer consistentlyPassing; + public Integer totalTestCases; + public Integer totalTestCasesLimited; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStatsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStatsParameter.java new file mode 100644 index 00000000..76864c2a --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestCasesStatsParameter.java @@ -0,0 +1,176 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/test-cases/stats}. */ +public class TestCasesStatsParameter { + private final String orgId; + private final String userId; + private final String groupId; + private final String teamId; + private final Interval interval; + private final String start; + private final String end; + private final String[] browser; + private final String[] build; + private final String[] device; + private final String[] os; + private final Status[] status; + private final String[] tag; + private final TagFilterMode tagFilterMode; + private final String[] automationBackend; + private final DeviceGroup deviceGroup; + + private TestCasesStatsParameter(Builder builder) { + orgId = builder.orgId; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + interval = builder.interval; + start = builder.start; + end = builder.end; + browser = builder.browser; + build = builder.build; + device = builder.device; + os = builder.os; + status = builder.status; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + automationBackend = builder.automationBackend; + deviceGroup = builder.deviceGroup; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("status", ParameterUtils.valuesOf(status, s -> s.value)), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend), + new AbstractMap.SimpleEntry<>("device_group", deviceGroup == null ? null : deviceGroup.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private String userId; + private String groupId; + private String teamId; + private Interval interval; + private String start; + private String end; + private String[] browser; + private String[] build; + private String[] device; + private String[] os; + private Status[] status; + private String[] tag; + private TagFilterMode tagFilterMode; + private String[] automationBackend; + private DeviceGroup deviceGroup; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setStatus(Status[] val) { + status = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public Builder setDeviceGroup(DeviceGroup val) { + deviceGroup = val; + return this; + } + + public TestCasesStatsParameter build() { + return new TestCasesStatsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestExecution.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestExecution.java new file mode 100644 index 00000000..5027f437 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestExecution.java @@ -0,0 +1,31 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; + +public class TestExecution { + public String id; + public String name; + public String status; + public String creationTime; + public String modificationTime; + public String error; + public Boolean passed; + public String browserNormalized; + public String osNormalized; + public String deviceName; + public String deviceGroup; + public String build; + public String automationBackend; + public Integer duration; + public List tags; + public String owner; + public String ancestor; + public String userId; + public String teamId; + public String groupId; + public String orgId; + public String startTime; + public String endTime; + public String deletionTime; + public Boolean isExpired; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestMetrics.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetrics.java new file mode 100644 index 00000000..7fb0b9d9 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetrics.java @@ -0,0 +1,6 @@ +package com.saucelabs.saucerest.model.insights; + +public class TestMetrics { + public Meta meta; + public TestMetricsAggs aggs; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsAggs.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsAggs.java new file mode 100644 index 00000000..3c4e0a6a --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsAggs.java @@ -0,0 +1,22 @@ +package com.saucelabs.saucerest.model.insights; + +import com.google.gson.annotations.SerializedName; +import java.util.Map; + +public class TestMetricsAggs { + public Integer count; + + @SerializedName("fastestRun") + public Item fastestRun; + + @SerializedName("slowestRun") + public Item slowestRun; + + public Map statuses; + + @SerializedName("totalQueueTime") + public Double totalQueueTime; + + @SerializedName("totalRunTime") + public Double totalRunTime; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsParameter.java new file mode 100644 index 00000000..80c7aacd --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestMetricsParameter.java @@ -0,0 +1,124 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v1/analytics/insights/test-metrics}. */ +public class TestMetricsParameter { + private final String query; + private final String start; + private final String end; + private final TimeRange timeRange; + private final Scope scope; + private final String[] owner; + private final TestResultParameter.Status status; + private final String[] os; + private final String[] browser; + + private TestMetricsParameter(Builder builder) { + query = builder.query; + start = builder.start; + end = builder.end; + timeRange = builder.timeRange; + scope = builder.scope; + owner = builder.owner; + status = builder.status; + os = builder.os; + browser = builder.browser; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("query", query), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("time_range", timeRange == null ? null : timeRange.toString()), + new AbstractMap.SimpleEntry<>("scope", scope == null ? null : scope.value), + new AbstractMap.SimpleEntry<>("owner", owner), + new AbstractMap.SimpleEntry<>("status", status == null ? null : status.getValue()), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("browser", browser) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String query; + private String start; + private String end; + private TimeRange timeRange; + private Scope scope; + private String[] owner; + private TestResultParameter.Status status; + private String[] os; + private String[] browser; + + /** @param query The name of the test to return metrics for (required). */ + public Builder(String query) { + this.query = query; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setTimeRange(TimeRange val) { + timeRange = val; + return this; + } + + public Builder setScope(Scope val) { + scope = val; + return this; + } + + /** Required when {@link #setScope(Scope)} is {@link Scope#SINGLE}. */ + public Builder setOwner(String[] val) { + owner = val; + return this; + } + + public Builder setStatus(TestResultParameter.Status val) { + status = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public TestMetricsParameter build() { + boolean isTimeRangeUsed = timeRange != null; + boolean isStartEndUsed = start != null && end != null; + + if (!isTimeRangeUsed && !isStartEndUsed) { + throw new IllegalStateException("Either 'time_range' or 'start' and 'end' must be set."); + } + if (isTimeRangeUsed && isStartEndUsed) { + throw new IllegalStateException("Only one of 'time_range' or 'start' and 'end' can be set, not both."); + } + + return new TestMetricsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestTrends.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestTrends.java new file mode 100644 index 00000000..07c45a80 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestTrends.java @@ -0,0 +1,10 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; +import java.util.Map; + +public class TestTrends { + public TrendsMeta meta; + public List buckets; + public Map> metrics; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Tests.java b/src/main/java/com/saucelabs/saucerest/model/insights/Tests.java new file mode 100644 index 00000000..13bf0e3f --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Tests.java @@ -0,0 +1,11 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; +import java.util.Map; + +public class Tests { + public List items; + public Integer total; + public Map statuses; + public Integer maxDuration; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestsParameter.java new file mode 100644 index 00000000..22032083 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestsParameter.java @@ -0,0 +1,212 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/tests}. */ +public class TestsParameter { + private final String orgId; + private final String userId; + private final String groupId; + private final String teamId; + private final String[] build; + private final String[] os; + private final String[] device; + private final String[] browser; + private final String[] tag; + private final TagFilterMode tagFilterMode; + private final String[] error; + private final String name; + private final String start; + private final String end; + private final Integer limit; + private final Integer offset; + private final TestsSortBy sortBy; + private final Sort sort; + private final String[] automationBackend; + private final DeviceGroup deviceGroup; + + private TestsParameter(Builder builder) { + orgId = builder.orgId; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + build = builder.build; + os = builder.os; + device = builder.device; + browser = builder.browser; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + error = builder.error; + name = builder.name; + start = builder.start; + end = builder.end; + limit = builder.limit; + offset = builder.offset; + sortBy = builder.sortBy; + sort = builder.sort; + automationBackend = builder.automationBackend; + deviceGroup = builder.deviceGroup; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value), + new AbstractMap.SimpleEntry<>("error", error), + new AbstractMap.SimpleEntry<>("name", name), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("limit", limit), + new AbstractMap.SimpleEntry<>("offset", offset), + new AbstractMap.SimpleEntry<>("sort_by", sortBy == null ? null : sortBy.value), + new AbstractMap.SimpleEntry<>("sort", sort == null ? null : sort.value), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend), + new AbstractMap.SimpleEntry<>("device_group", deviceGroup == null ? null : deviceGroup.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private String userId; + private String groupId; + private String teamId; + private String[] build; + private String[] os; + private String[] device; + private String[] browser; + private String[] tag; + private TagFilterMode tagFilterMode; + private String[] error; + private String name; + private String start; + private String end; + private Integer limit; + private Integer offset; + private TestsSortBy sortBy; + private Sort sort; + private String[] automationBackend; + private DeviceGroup deviceGroup; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public Builder setError(String[] val) { + error = val; + return this; + } + + public Builder setName(String val) { + name = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setLimit(Integer val) { + limit = val; + return this; + } + + public Builder setOffset(Integer val) { + offset = val; + return this; + } + + public Builder setSortBy(TestsSortBy val) { + sortBy = val; + return this; + } + + public Builder setSort(Sort val) { + sort = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public Builder setDeviceGroup(DeviceGroup val) { + deviceGroup = val; + return this; + } + + public TestsParameter build() { + return new TestsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TestsSortBy.java b/src/main/java/com/saucelabs/saucerest/model/insights/TestsSortBy.java new file mode 100644 index 00000000..fbc98535 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TestsSortBy.java @@ -0,0 +1,12 @@ +package com.saucelabs.saucerest.model.insights; + +public enum TestsSortBy { + DURATION("duration"), + CREATION_TIME("creation_time"); + + public final String value; + + TestsSortBy(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TimeRange.java b/src/main/java/com/saucelabs/saucerest/model/insights/TimeRange.java new file mode 100644 index 00000000..5b247d26 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TimeRange.java @@ -0,0 +1,17 @@ +package com.saucelabs.saucerest.model.insights; + +/** A relative time window, measured backward from now, e.g. {@code new TimeRange(7, Unit.D)} for the last 7 days. */ +public class TimeRange { + private final int value; + private final Unit unit; + + public TimeRange(int value, Unit unit) { + this.value = value; + this.unit = unit; + } + + @Override + public String toString() { + return String.format("%d%s", value, unit.value); + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TrendBucket.java b/src/main/java/com/saucelabs/saucerest/model/insights/TrendBucket.java new file mode 100644 index 00000000..9dfd2b88 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TrendBucket.java @@ -0,0 +1,8 @@ +package com.saucelabs.saucerest.model.insights; + +public class TrendBucket { + public Long timestamp; + public String datetime; + public Integer count; + public Aggs aggs; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrors.java b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrors.java new file mode 100644 index 00000000..e4069721 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrors.java @@ -0,0 +1,9 @@ +package com.saucelabs.saucerest.model.insights; + +import java.util.List; + +public class TrendsErrors { + public TrendsMeta meta; + public List buckets; + public Integer allItemsCount; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrorsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrorsParameter.java new file mode 100644 index 00000000..8498dd1d --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsErrorsParameter.java @@ -0,0 +1,176 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/trends/errors}. */ +public class TrendsErrorsParameter { + private final String orgId; + private final Interval interval; + private final String userId; + private final String groupId; + private final String teamId; + private final String start; + private final String end; + private final String[] browser; + private final String[] build; + private final String[] device; + private final String[] os; + private final Status[] status; + private final String[] tag; + private final TagFilterMode tagFilterMode; + private final String[] automationBackend; + private final DeviceGroup deviceGroup; + + private TrendsErrorsParameter(Builder builder) { + orgId = builder.orgId; + interval = builder.interval; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + start = builder.start; + end = builder.end; + browser = builder.browser; + build = builder.build; + device = builder.device; + os = builder.os; + status = builder.status; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + automationBackend = builder.automationBackend; + deviceGroup = builder.deviceGroup; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("status", ParameterUtils.valuesOf(status, s -> s.value)), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend), + new AbstractMap.SimpleEntry<>("device_group", deviceGroup == null ? null : deviceGroup.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private Interval interval; + private String userId; + private String groupId; + private String teamId; + private String start; + private String end; + private String[] browser; + private String[] build; + private String[] device; + private String[] os; + private Status[] status; + private String[] tag; + private TagFilterMode tagFilterMode; + private String[] automationBackend; + private DeviceGroup deviceGroup; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setStatus(Status[] val) { + status = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public Builder setDeviceGroup(DeviceGroup val) { + deviceGroup = val; + return this; + } + + public TrendsErrorsParameter build() { + return new TrendsErrorsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TrendsMeta.java b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsMeta.java new file mode 100644 index 00000000..731cb711 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsMeta.java @@ -0,0 +1,5 @@ +package com.saucelabs.saucerest.model.insights; + +public class TrendsMeta { + public String status; +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/TrendsTestsParameter.java b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsTestsParameter.java new file mode 100644 index 00000000..28210eb3 --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/TrendsTestsParameter.java @@ -0,0 +1,186 @@ +package com.saucelabs.saucerest.model.insights; + +import java.time.LocalDateTime; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** Query parameters for {@code GET /v2/insights/{source}/trends/tests}. */ +public class TrendsTestsParameter { + private final String orgId; + private final Interval interval; + private final String timeZone; + private final String userId; + private final String groupId; + private final String teamId; + private final String start; + private final String end; + private final String[] browser; + private final String[] build; + private final String[] device; + private final String[] os; + private final Status[] status; + private final String[] tag; + private final TagFilterMode tagFilterMode; + private final String[] automationBackend; + private final DeviceGroup deviceGroup; + + private TrendsTestsParameter(Builder builder) { + orgId = builder.orgId; + interval = builder.interval; + timeZone = builder.timeZone; + userId = builder.userId; + groupId = builder.groupId; + teamId = builder.teamId; + start = builder.start; + end = builder.end; + browser = builder.browser; + build = builder.build; + device = builder.device; + os = builder.os; + status = builder.status; + tag = builder.tag; + tagFilterMode = builder.tagFilterMode; + automationBackend = builder.automationBackend; + deviceGroup = builder.deviceGroup; + } + + public Map toMap() { + Map parameters = new HashMap<>(); + + Stream.of( + new AbstractMap.SimpleEntry<>("org_id", orgId), + new AbstractMap.SimpleEntry<>("interval", interval == null ? null : interval.value), + new AbstractMap.SimpleEntry<>("time_zone", timeZone), + new AbstractMap.SimpleEntry<>("user_id", userId), + new AbstractMap.SimpleEntry<>("group_id", groupId), + new AbstractMap.SimpleEntry<>("team_id", teamId), + new AbstractMap.SimpleEntry<>("start", start), + new AbstractMap.SimpleEntry<>("end", end), + new AbstractMap.SimpleEntry<>("browser", browser), + new AbstractMap.SimpleEntry<>("build", build), + new AbstractMap.SimpleEntry<>("device", device), + new AbstractMap.SimpleEntry<>("os", os), + new AbstractMap.SimpleEntry<>("status", ParameterUtils.valuesOf(status, s -> s.value)), + new AbstractMap.SimpleEntry<>("tag", tag), + new AbstractMap.SimpleEntry<>("tag_filter_mode", tagFilterMode == null ? null : tagFilterMode.value), + new AbstractMap.SimpleEntry<>("automation_backend", automationBackend), + new AbstractMap.SimpleEntry<>("device_group", deviceGroup == null ? null : deviceGroup.value) + ) + .filter(e -> e.getValue() != null) + .forEach(e -> parameters.put(e.getKey(), e.getValue())); + + return parameters; + } + + public static final class Builder { + private final String orgId; + private Interval interval; + private String timeZone; + private String userId; + private String groupId; + private String teamId; + private String start; + private String end; + private String[] browser; + private String[] build; + private String[] device; + private String[] os; + private Status[] status; + private String[] tag; + private TagFilterMode tagFilterMode; + private String[] automationBackend; + private DeviceGroup deviceGroup; + + /** @param orgId The organization to return results for (required). */ + public Builder(String orgId) { + this.orgId = orgId; + } + + public Builder setInterval(Interval val) { + interval = val; + return this; + } + + /** @param val UTC offset, e.g. {@code "+01:00"} (default: {@code "+00:00"}). */ + public Builder setTimeZone(String val) { + timeZone = val; + return this; + } + + public Builder setUserId(String val) { + userId = val; + return this; + } + + public Builder setGroupId(String val) { + groupId = val; + return this; + } + + public Builder setTeamId(String val) { + teamId = val; + return this; + } + + public Builder setStart(LocalDateTime val) { + start = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setEnd(LocalDateTime val) { + end = DateTimeUtils.toUtcString(val); + return this; + } + + public Builder setBrowser(String[] val) { + browser = val; + return this; + } + + public Builder setBuild(String[] val) { + build = val; + return this; + } + + public Builder setDevice(String[] val) { + device = val; + return this; + } + + public Builder setOs(String[] val) { + os = val; + return this; + } + + public Builder setStatus(Status[] val) { + status = val; + return this; + } + + public Builder setTag(String[] val) { + tag = val; + return this; + } + + public Builder setTagFilterMode(TagFilterMode val) { + tagFilterMode = val; + return this; + } + + public Builder setAutomationBackend(String[] val) { + automationBackend = val; + return this; + } + + public Builder setDeviceGroup(DeviceGroup val) { + deviceGroup = val; + return this; + } + + public TrendsTestsParameter build() { + return new TrendsTestsParameter(this); + } + } +} diff --git a/src/main/java/com/saucelabs/saucerest/model/insights/Unit.java b/src/main/java/com/saucelabs/saucerest/model/insights/Unit.java new file mode 100644 index 00000000..21c5366f --- /dev/null +++ b/src/main/java/com/saucelabs/saucerest/model/insights/Unit.java @@ -0,0 +1,14 @@ +package com.saucelabs.saucerest.model.insights; + +public enum Unit { + D("d"), + H("h"), + M("m"), + S("s"); + + public final String value; + + Unit(String value) { + this.value = value; + } +} diff --git a/src/test/java/com/saucelabs/saucerest/integration/InsightsEndpointTest.java b/src/test/java/com/saucelabs/saucerest/integration/InsightsEndpointTest.java index 470a49f5..3b0a6336 100644 --- a/src/test/java/com/saucelabs/saucerest/integration/InsightsEndpointTest.java +++ b/src/test/java/com/saucelabs/saucerest/integration/InsightsEndpointTest.java @@ -1,18 +1,41 @@ package com.saucelabs.saucerest.integration; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.saucelabs.saucerest.DataCenter; +import com.saucelabs.saucerest.JobSource; import com.saucelabs.saucerest.SauceREST; +import com.saucelabs.saucerest.api.AccountsEndpoint; import com.saucelabs.saucerest.api.InsightsEndpoint; +import com.saucelabs.saucerest.model.insights.AnalyticsTrendsParameter; +import com.saucelabs.saucerest.model.insights.Errors; +import com.saucelabs.saucerest.model.insights.ErrorsParameter; +import com.saucelabs.saucerest.model.insights.TestCases; +import com.saucelabs.saucerest.model.insights.TestCasesParameter; +import com.saucelabs.saucerest.model.insights.TestCasesStats; +import com.saucelabs.saucerest.model.insights.TestCasesStatsParameter; +import com.saucelabs.saucerest.model.insights.TestMetrics; +import com.saucelabs.saucerest.model.insights.TestMetricsParameter; import com.saucelabs.saucerest.model.insights.TestResult; import com.saucelabs.saucerest.model.insights.TestResultParameter; +import com.saucelabs.saucerest.model.insights.Tests; +import com.saucelabs.saucerest.model.insights.TestTrends; +import com.saucelabs.saucerest.model.insights.TestsParameter; +import com.saucelabs.saucerest.model.insights.TrendsErrors; +import com.saucelabs.saucerest.model.insights.TrendsErrorsParameter; +import com.saucelabs.saucerest.model.insights.TrendsTestsParameter; import java.io.IOException; import java.time.LocalDateTime; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; public class InsightsEndpointTest { + private static String getOrgId(SauceREST sauceREST) throws IOException { + AccountsEndpoint accountsEndpoint = sauceREST.getAccountsEndpoint(); + return accountsEndpoint.getOrganization().results.get(0).id; + } + @ParameterizedTest @EnumSource(DataCenter.class) public void getTestResultTest(DataCenter dataCenter) throws IOException { @@ -30,4 +53,124 @@ public void getTestResultTest(DataCenter dataCenter) throws IOException { assertTrue(testResult.items.size() > 0); } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTestMetricsSummaryTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TestMetricsParameter parameter = new TestMetricsParameter.Builder("AnalyticsSeleniumTest") + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TestMetrics testMetrics = insightsEndpoint.getTestMetricsSummary(parameter); + + assertNotNull(testMetrics.aggs); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTestTrendsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + AnalyticsTrendsParameter parameter = new AnalyticsTrendsParameter.Builder() + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TestTrends testTrends = insightsEndpoint.getTestTrends(parameter); + + assertNotNull(testTrends.buckets); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTestsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TestsParameter parameter = new TestsParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + Tests tests = insightsEndpoint.getTests(JobSource.VDC, parameter); + + assertNotNull(tests.items); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTestCasesTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TestCasesParameter parameter = new TestCasesParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TestCases testCases = insightsEndpoint.getTestCases(JobSource.VDC, parameter); + + assertNotNull(testCases.testCases); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTestCasesStatsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TestCasesStatsParameter parameter = new TestCasesStatsParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TestCasesStats testCasesStats = insightsEndpoint.getTestCasesStats(JobSource.VDC, parameter); + + assertNotNull(testCasesStats); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getErrorsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + ErrorsParameter parameter = new ErrorsParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + Errors errors = insightsEndpoint.getErrors(JobSource.VDC, parameter); + + assertNotNull(errors.buckets); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTrendsTestsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TrendsTestsParameter parameter = new TrendsTestsParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TestTrends testTrends = insightsEndpoint.getTrendsTests(JobSource.VDC, parameter); + + assertNotNull(testTrends.buckets); + } + + @ParameterizedTest + @EnumSource(DataCenter.class) + public void getTrendsErrorsTest(DataCenter dataCenter) throws IOException { + SauceREST sauceREST = new SauceREST(dataCenter); + InsightsEndpoint insightsEndpoint = sauceREST.getInsightsEndpoint(); + + TrendsErrorsParameter parameter = new TrendsErrorsParameter.Builder(getOrgId(sauceREST)) + .setStart(LocalDateTime.now().minusDays(30)) + .setEnd(LocalDateTime.now()) + .build(); + TrendsErrors trendsErrors = insightsEndpoint.getTrendsErrors(JobSource.VDC, parameter); + + assertNotNull(trendsErrors.buckets); + } } \ No newline at end of file diff --git a/src/test/java/com/saucelabs/saucerest/unit/AnalyticsTrendsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/AnalyticsTrendsParameterTest.java new file mode 100644 index 00000000..dee4b014 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/AnalyticsTrendsParameterTest.java @@ -0,0 +1,31 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.saucelabs.saucerest.model.insights.AnalyticsTrendsParameter; +import com.saucelabs.saucerest.model.insights.Interval; +import com.saucelabs.saucerest.model.insights.TimeRange; +import com.saucelabs.saucerest.model.insights.Unit; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class AnalyticsTrendsParameterTest { + + @Test + public void testToMapWithTimeRange() { + AnalyticsTrendsParameter params = new AnalyticsTrendsParameter.Builder() + .setTimeRange(new TimeRange(1, Unit.H)) + .setInterval(Interval.FIFTEEN_MINUTES) + .build(); + + Map map = params.toMap(); + assertEquals("1h", map.get("time_range")); + assertEquals("15m", map.get("interval")); + } + + @Test + public void testBuildThrowsWhenNeitherTimeRangeNorStartEndSet() { + assertThrows(IllegalStateException.class, () -> new AnalyticsTrendsParameter.Builder().build()); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/ErrorsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/ErrorsParameterTest.java new file mode 100644 index 00000000..920c5f5c --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/ErrorsParameterTest.java @@ -0,0 +1,37 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.saucelabs.saucerest.model.insights.ErrorsParameter; +import com.saucelabs.saucerest.model.insights.Interval; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class ErrorsParameterTest { + + @Test + public void testToMap() { + ErrorsParameter params = new ErrorsParameter.Builder("org123") + .setInterval(Interval.ONE_HOUR) + .setUserId("user123") + .setLimit(10) + .setOffset(0) + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("1h", map.get("interval")); + assertEquals("user123", map.get("user_id")); + assertEquals(10, map.get("limit")); + assertEquals(0, map.get("offset")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + ErrorsParameter params = new ErrorsParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TestCasesParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TestCasesParameterTest.java new file mode 100644 index 00000000..903b8b91 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TestCasesParameterTest.java @@ -0,0 +1,45 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.saucelabs.saucerest.model.insights.Interval; +import com.saucelabs.saucerest.model.insights.Sort; +import com.saucelabs.saucerest.model.insights.Status; +import com.saucelabs.saucerest.model.insights.TestCasesParameter; +import com.saucelabs.saucerest.model.insights.TestCasesSortBy; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TestCasesParameterTest { + + @Test + public void testToMap() { + TestCasesParameter params = new TestCasesParameter.Builder("org123") + .setInterval(Interval.SEVEN_DAYS) + .setStatus(new Status[] {Status.PASSED, Status.FAILED}) + .setLimit(25) + .setOffset(5) + .setSortBy(TestCasesSortBy.AVG_DURATION) + .setSort(Sort.DESC) + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("7d", map.get("interval")); + assertArrayEquals(new String[] {"passed", "failed"}, (String[]) map.get("status")); + assertEquals(25, map.get("limit")); + assertEquals(5, map.get("offset")); + assertEquals("avg_duration", map.get("sort_by")); + assertEquals("desc", map.get("sort")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + TestCasesParameter params = new TestCasesParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TestCasesStatsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TestCasesStatsParameterTest.java new file mode 100644 index 00000000..1aea4598 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TestCasesStatsParameterTest.java @@ -0,0 +1,34 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.saucelabs.saucerest.model.insights.DeviceGroup; +import com.saucelabs.saucerest.model.insights.Interval; +import com.saucelabs.saucerest.model.insights.TestCasesStatsParameter; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TestCasesStatsParameterTest { + + @Test + public void testToMap() { + TestCasesStatsParameter params = new TestCasesStatsParameter.Builder("org123") + .setInterval(Interval.ONE_DAY) + .setDeviceGroup(DeviceGroup.PRIVATE) + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("1d", map.get("interval")); + assertEquals("private", map.get("device_group")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + TestCasesStatsParameter params = new TestCasesStatsParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TestMetricsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TestMetricsParameterTest.java new file mode 100644 index 00000000..3548fbe4 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TestMetricsParameterTest.java @@ -0,0 +1,61 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.saucelabs.saucerest.model.insights.Scope; +import com.saucelabs.saucerest.model.insights.TestMetricsParameter; +import com.saucelabs.saucerest.model.insights.TestResultParameter; +import com.saucelabs.saucerest.model.insights.TimeRange; +import com.saucelabs.saucerest.model.insights.Unit; +import java.time.LocalDateTime; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TestMetricsParameterTest { + + @Test + public void testToMapWithStartAndEnd() { + TestMetricsParameter params = new TestMetricsParameter.Builder("my test") + .setStart(LocalDateTime.of(2024, 1, 1, 0, 0, 0)) + .setEnd(LocalDateTime.of(2024, 1, 2, 0, 0, 0)) + .setScope(Scope.ORGANIZATION) + .setStatus(TestResultParameter.Status.PASSED) + .build(); + + Map map = params.toMap(); + assertEquals("my test", map.get("query")); + assertEquals("organization", map.get("scope")); + assertEquals("passed", map.get("status")); + assertTrue(((String) map.get("start")).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")); + assertTrue(((String) map.get("end")).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")); + } + + @Test + public void testToMapWithTimeRange() { + TestMetricsParameter params = new TestMetricsParameter.Builder("my test") + .setTimeRange(new TimeRange(7, Unit.D)) + .build(); + + Map map = params.toMap(); + assertEquals("my test", map.get("query")); + assertEquals("7d", map.get("time_range")); + } + + @Test + public void testBuildThrowsWhenNeitherTimeRangeNorStartEndSet() { + assertThrows(IllegalStateException.class, () -> new TestMetricsParameter.Builder("my test").build()); + } + + @Test + public void testBuildThrowsWhenBothTimeRangeAndStartEndSet() { + assertThrows( + IllegalStateException.class, + () -> new TestMetricsParameter.Builder("my test") + .setStart(LocalDateTime.of(2024, 1, 1, 0, 0, 0)) + .setEnd(LocalDateTime.of(2024, 1, 2, 0, 0, 0)) + .setTimeRange(new TimeRange(7, Unit.D)) + .build()); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TestsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TestsParameterTest.java new file mode 100644 index 00000000..300740e2 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TestsParameterTest.java @@ -0,0 +1,65 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.saucelabs.saucerest.model.insights.DeviceGroup; +import com.saucelabs.saucerest.model.insights.Sort; +import com.saucelabs.saucerest.model.insights.TagFilterMode; +import com.saucelabs.saucerest.model.insights.TestsParameter; +import com.saucelabs.saucerest.model.insights.TestsSortBy; +import java.time.LocalDateTime; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TestsParameterTest { + + @Test + public void testToMap() { + TestsParameter params = new TestsParameter.Builder("org123") + .setUserId("user123") + .setGroupId("group123") + .setTeamId("team123") + .setBuild(new String[] {"build1"}) + .setOs(new String[] {"Windows 10"}) + .setDevice(new String[] {"iPhone"}) + .setBrowser(new String[] {"chrome"}) + .setTag(new String[] {"smoke"}) + .setTagFilterMode(TagFilterMode.AND) + .setError(new String[] {"timeout"}) + .setName("my test") + .setStart(LocalDateTime.of(2024, 1, 1, 0, 0, 0)) + .setEnd(LocalDateTime.of(2024, 1, 2, 0, 0, 0)) + .setLimit(50) + .setOffset(0) + .setSortBy(TestsSortBy.DURATION) + .setSort(Sort.ASC) + .setAutomationBackend(new String[] {"webdriver"}) + .setDeviceGroup(DeviceGroup.PUBLIC) + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("user123", map.get("user_id")); + assertEquals("group123", map.get("group_id")); + assertEquals("team123", map.get("team_id")); + assertEquals("and", map.get("tag_filter_mode")); + assertEquals("my test", map.get("name")); + assertEquals(50, map.get("limit")); + assertEquals(0, map.get("offset")); + assertEquals("duration", map.get("sort_by")); + assertEquals("asc", map.get("sort")); + assertEquals("public", map.get("device_group")); + assertTrue(((String) map.get("start")).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")); + assertTrue(((String) map.get("end")).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + TestsParameter params = new TestsParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TrendsErrorsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TrendsErrorsParameterTest.java new file mode 100644 index 00000000..b5851ade --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TrendsErrorsParameterTest.java @@ -0,0 +1,33 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.saucelabs.saucerest.model.insights.Interval; +import com.saucelabs.saucerest.model.insights.TrendsErrorsParameter; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TrendsErrorsParameterTest { + + @Test + public void testToMap() { + TrendsErrorsParameter params = new TrendsErrorsParameter.Builder("org123") + .setInterval(Interval.THIRTY_DAYS) + .setTeamId("team123") + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("30d", map.get("interval")); + assertEquals("team123", map.get("team_id")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + TrendsErrorsParameter params = new TrendsErrorsParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +} diff --git a/src/test/java/com/saucelabs/saucerest/unit/TrendsTestsParameterTest.java b/src/test/java/com/saucelabs/saucerest/unit/TrendsTestsParameterTest.java new file mode 100644 index 00000000..d986c6c2 --- /dev/null +++ b/src/test/java/com/saucelabs/saucerest/unit/TrendsTestsParameterTest.java @@ -0,0 +1,33 @@ +package com.saucelabs.saucerest.unit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.saucelabs.saucerest.model.insights.Interval; +import com.saucelabs.saucerest.model.insights.TrendsTestsParameter; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TrendsTestsParameterTest { + + @Test + public void testToMap() { + TrendsTestsParameter params = new TrendsTestsParameter.Builder("org123") + .setInterval(Interval.SIX_HOURS) + .setTimeZone("+01:00") + .build(); + + Map map = params.toMap(); + assertEquals("org123", map.get("org_id")); + assertEquals("6h", map.get("interval")); + assertEquals("+01:00", map.get("time_zone")); + } + + @Test + public void testToMapWithOnlyRequiredValues() { + TrendsTestsParameter params = new TrendsTestsParameter.Builder("org123").build(); + + Map map = params.toMap(); + assertEquals(1, map.size()); + assertEquals("org123", map.get("org_id")); + } +}