diff --git a/build.gradle b/build.gradle index 94d40bb..a60b27c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'io.visual-regression-tracker.sdk-java' -version '4.0.2' +version '4.0.3' apply plugin: 'java' apply plugin: 'jacoco' diff --git a/src/main/java/io/visual_regression_tracker/sdk_java/VisualRegressionTracker.java b/src/main/java/io/visual_regression_tracker/sdk_java/VisualRegressionTracker.java index 30c2640..3af3145 100644 --- a/src/main/java/io/visual_regression_tracker/sdk_java/VisualRegressionTracker.java +++ b/src/main/java/io/visual_regression_tracker/sdk_java/VisualRegressionTracker.java @@ -15,22 +15,19 @@ import java.util.Optional; public class VisualRegressionTracker { - private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); - String apiKeyHeaderName = "apiKey"; - Gson gson = new Gson(); - VisualRegressionTrackerConfig visualRegressionTrackerConfig; - String buildId; - String projectId; - OkHttpClient client; + protected static final String apiKeyHeaderName = "apiKey"; + protected static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); + protected Gson gson; + protected VisualRegressionTrackerConfig visualRegressionTrackerConfig; + protected String buildId; + protected String projectId; + protected OkHttpClient client; public VisualRegressionTracker(VisualRegressionTrackerConfig visualRegressionTrackerConfig) { this.visualRegressionTrackerConfig = visualRegressionTrackerConfig; this.client = new OkHttpClient(); - } - - protected boolean isStarted() { - return this.buildId != null && this.projectId != null; + this.gson = new Gson(); } public void start() throws IOException { @@ -48,20 +45,9 @@ public void start() throws IOException { .build(); try (Response response = client.newCall(request).execute()) { - if (response.code() == 401) { - throw new TestRunException("Unauthorized"); - } - if (response.code() == 403) { - throw new TestRunException("Api key not authenticated"); - } - if (response.code() == 404) { - throw new TestRunException("Project not found"); - } - - String responseBody = Optional.ofNullable(response.body()) - .orElseThrow(() -> new TestRunException("Cannot get response body")) - .string(); - BuildResponse buildDTO = gson.fromJson(responseBody, BuildResponse.class); + + BuildResponse buildDTO = handleResponse(response, BuildResponse.class); + this.buildId = Optional.ofNullable(buildDTO.getId()) .orElseThrow(() -> new TestRunException("Build id is null")); this.projectId = Optional.ofNullable(buildDTO.getProjectId()) @@ -80,7 +66,32 @@ public void stop() throws IOException { .patch(RequestBody.create(JSON, "")) .build(); - client.newCall(request).execute(); + try (Response response = client.newCall(request).execute()) { + handleResponse(response, Object.class); + } + } + + public void track(String name, String imageBase64, TestRunOptions testRunOptions) throws IOException { + TestRunResponse testResultDTO = this.submitTestRun(name, imageBase64, testRunOptions); + + TestRunStatus status = Optional.ofNullable(testResultDTO.getStatus()) + .orElseThrow(() -> new TestRunException("Status is null")); + + if (status.equals(TestRunStatus.NEW)) { + throw new TestRunException("No baseline: ".concat(testResultDTO.getUrl())); + } + + if (status.equals(TestRunStatus.UNRESOLVED)) { + throw new TestRunException("Difference found: ".concat(testResultDTO.getUrl())); + } + } + + public void track(String name, String imageBase64) throws IOException { + this.track(name, imageBase64, TestRunOptions.builder().build()); + } + + protected boolean isStarted() { + return this.buildId != null && this.projectId != null; } protected TestRunResponse submitTestRun(String name, String imageBase64, TestRunOptions testRunOptions) throws IOException { @@ -110,29 +121,19 @@ protected TestRunResponse submitTestRun(String name, String imageBase64, TestRun .build(); try (Response response = client.newCall(request).execute()) { - String responseBody = Optional.ofNullable(response.body()) - .orElseThrow(() -> new TestRunException("Cannot get response body")) - .string(); - return gson.fromJson(responseBody, TestRunResponse.class); + return handleResponse(response, TestRunResponse.class); } } - public void track(String name, String imageBase64, TestRunOptions testRunOptions) throws IOException { - TestRunResponse testResultDTO = this.submitTestRun(name, imageBase64, testRunOptions); - - TestRunStatus status = Optional.ofNullable(testResultDTO.getStatus()) - .orElseThrow(() -> new TestRunException("Status is null")); + protected T handleResponse(Response response, Class classOfT) throws IOException { + String responseBody = Optional.ofNullable(response.body()) + .orElseThrow(() -> new TestRunException("Cannot get response body")) + .string(); - if (status.equals(TestRunStatus.NEW)) { - throw new TestRunException("No baseline: ".concat(testResultDTO.getUrl())); + if (!response.isSuccessful()) { + throw new TestRunException(responseBody); } - if (status.equals(TestRunStatus.UNRESOLVED)) { - throw new TestRunException("Difference found: ".concat(testResultDTO.getUrl())); - } - } - - public void track(String name, String imageBase64) throws IOException { - this.track(name, imageBase64, TestRunOptions.builder().build()); + return gson.fromJson(responseBody, classOfT); } } diff --git a/src/test/java/io/visual_regression_tracker/sdk_java/VisualRegressionTrackerTest.java b/src/test/java/io/visual_regression_tracker/sdk_java/VisualRegressionTrackerTest.java index a067a00..0c6554d 100644 --- a/src/test/java/io/visual_regression_tracker/sdk_java/VisualRegressionTrackerTest.java +++ b/src/test/java/io/visual_regression_tracker/sdk_java/VisualRegressionTrackerTest.java @@ -6,6 +6,10 @@ import io.visual_regression_tracker.sdk_java.response.BuildResponse; import io.visual_regression_tracker.sdk_java.response.TestRunResponse; import lombok.SneakyThrows; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -48,25 +52,6 @@ public void tearDown() { server.shutdown(); } - @DataProvider(name = "shouldReturnIsStartedCases") - public Object[][] shouldReturnIsStartedCases() { - return new Object[][]{ - {null, null, false}, - {null, "some", false}, - {"some", null, false}, - {"some", "some", true}, - }; - } - - @Test(dataProvider = "shouldReturnIsStartedCases") - public void shouldReturnIsStarted(String buildId, String projectId, boolean expectedResult) { - vrt.buildId = buildId; - vrt.projectId = projectId; - - boolean result = vrt.isStarted(); - MatcherAssert.assertThat(result, CoreMatchers.is(expectedResult)); - } - @Test public void shouldStartBuild() throws IOException, InterruptedException { String buildId = "123123"; @@ -86,57 +71,12 @@ public void shouldStartBuild() throws IOException, InterruptedException { vrt.start(); RecordedRequest request = server.takeRequest(); - MatcherAssert.assertThat(request.getHeader(vrt.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); + MatcherAssert.assertThat(request.getHeader(VisualRegressionTracker.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); MatcherAssert.assertThat(request.getBody().readUtf8(), CoreMatchers.is(gson.toJson(buildRequest))); MatcherAssert.assertThat(vrt.buildId, CoreMatchers.is(buildId)); MatcherAssert.assertThat(vrt.projectId, CoreMatchers.is(projectId)); } - @Test - public void shouldThrowExceptionIfProjectNotFound() throws IOException { - server.enqueue(new MockResponse() - .setResponseCode(404) - .setBody("{\r\n \"statusCode\": 404,\r\n \"message\": \"Project not found\"\r\n}")); - - String exceptionMessage = ""; - try { - vrt.start(); - } catch (TestRunException ex) { - exceptionMessage = ex.getMessage(); - } - MatcherAssert.assertThat(exceptionMessage, CoreMatchers.is("Project not found")); - } - - @Test - public void shouldThrowExceptionIfUnauthorized() throws IOException { - server.enqueue(new MockResponse() - .setResponseCode(401) - .setBody("{\r\n \"statusCode\": 401,\r\n \"message\": \"Unauthorized\"\r\n}")); - - String exceptionMessage = ""; - try { - vrt.start(); - } catch (TestRunException ex) { - exceptionMessage = ex.getMessage(); - } - MatcherAssert.assertThat(exceptionMessage, CoreMatchers.is("Unauthorized")); - } - - @Test - public void shouldThrowExceptionIfForbidden() throws IOException { - server.enqueue(new MockResponse() - .setResponseCode(403) - .setBody("{\r\n \"statusCode\": 403,\r\n \"message\": \"Forbidden\"\r\n}")); - - String exceptionMessage = ""; - try { - vrt.start(); - } catch (TestRunException ex) { - exceptionMessage = ex.getMessage(); - } - MatcherAssert.assertThat(exceptionMessage, CoreMatchers.is("Api key not authenticated")); - } - @Test public void shouldStopBuild() throws IOException, InterruptedException { String buildId = "123123"; @@ -154,7 +94,7 @@ public void shouldStopBuild() throws IOException, InterruptedException { RecordedRequest request = server.takeRequest(); MatcherAssert.assertThat(request.getMethod(), CoreMatchers.is("PATCH")); - MatcherAssert.assertThat(request.getHeader(vrt.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); + MatcherAssert.assertThat(request.getHeader(VisualRegressionTracker.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); MatcherAssert.assertThat(Objects.requireNonNull(request.getRequestUrl()).encodedPath(), CoreMatchers.containsString(buildId)); } @@ -204,13 +144,13 @@ public void shouldSubmitTestRun() throws IOException, InterruptedException { TestRunResponse result = vrt.submitTestRun(name, imageBase64, testRunOptions); RecordedRequest request = server.takeRequest(); - MatcherAssert.assertThat(request.getHeader(vrt.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); + MatcherAssert.assertThat(request.getHeader(VisualRegressionTracker.apiKeyHeaderName), CoreMatchers.is(config.getApiKey())); MatcherAssert.assertThat(request.getBody().readUtf8(), CoreMatchers.is(gson.toJson(testRunRequest))); MatcherAssert.assertThat(gson.toJson(result), CoreMatchers.is(gson.toJson(testRunResponse))); } @Test - public void shouldNotSubmitTestRunIfNotStarted() throws IOException { + public void submitTestRunShouldThrowIfNotStarted() throws IOException { VisualRegressionTracker vrtMocked = Mockito.mock(VisualRegressionTracker.class); Mockito.when(vrtMocked.isStarted()).thenReturn(false); @@ -224,8 +164,8 @@ public void shouldNotSubmitTestRunIfNotStarted() throws IOException { MatcherAssert.assertThat(exceptionMessage, CoreMatchers.is("Visual Regression Tracker has not been started")); } - @DataProvider(name = "shouldTrackThrowExceptionCases") - public Object[][] shouldTrackThrowExceptionCases() { + @DataProvider(name = "trackShouldThrowExceptionCases") + public Object[][] trackShouldThrowExceptionCases() { return new Object[][]{ { TestRunResponse.builder() @@ -244,8 +184,8 @@ public Object[][] shouldTrackThrowExceptionCases() { }; } - @Test(dataProvider = "shouldTrackThrowExceptionCases") - public void shouldTrackThrowException(TestRunResponse testRunResponse, String expectedExceptionMessage) throws IOException { + @Test(dataProvider = "trackShouldThrowExceptionCases") + public void trackShouldThrowException(TestRunResponse testRunResponse, String expectedExceptionMessage) throws IOException { VisualRegressionTracker vrtMocked = Mockito.mock(VisualRegressionTracker.class); Mockito.when(vrtMocked.submitTestRun(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(testRunResponse); @@ -289,4 +229,50 @@ public void shouldTrackOverload() throws IOException { Mockito.verify(vrtMocked, Mockito.times(1)).track(Mockito.anyString(), Mockito.anyString(), Mockito.any(TestRunOptions.class)); } + + @DataProvider(name = "shouldReturnIsStartedCases") + public Object[][] shouldReturnIsStartedCases() { + return new Object[][]{ + {null, null, false}, + {null, "some", false}, + {"some", null, false}, + {"some", "some", true}, + }; + } + + @Test(dataProvider = "shouldReturnIsStartedCases") + public void shouldReturnIsStarted(String buildId, String projectId, boolean expectedResult) { + vrt.buildId = buildId; + vrt.projectId = projectId; + + boolean result = vrt.isStarted(); + + MatcherAssert.assertThat(result, CoreMatchers.is(expectedResult)); + } + + @Test + public void handleRequestShouldThrowIfNotSuccess() throws IOException { + String error = "{\n" + + " \"statusCode\": 404,\n" + + " \"message\": \"Project not found\"\n" + + "}"; + Request mockRequest = new Request.Builder() + .url(config.getApiUrl()) + .build(); + + String exceptionMessage = ""; + try { + vrt.handleResponse(new Response.Builder() + .request(mockRequest) + .protocol(Protocol.HTTP_2) + .code(401) + .message("Not found") + .body(ResponseBody.create(error, VisualRegressionTracker.JSON)) + .build(), Object.class); + } catch (TestRunException ex) { + exceptionMessage = ex.getMessage(); + } + + MatcherAssert.assertThat(exceptionMessage, CoreMatchers.is(error)); + } }