8000 Make resource validation fail-fast safety by draker94 · Pull Request #3791 · vividus-framework/vividus · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Make resource validation fail-fast safety #3791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ou 8000 r terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,17 +25,20 @@
import org.vividus.http.validation.ResourceValidator;
import org.vividus.http.validation.model.ResourceValidation;
import org.vividus.reporter.event.AttachmentPublisher;
import org.vividus.softassert.ISoftAssert;
import org.vividus.util.UriUtils;

public class HttpResourceValidationSteps
{
private final ResourceValidator<ResourceValidation> resourceValidator;
private final ISoftAssert softAssert;
private final AttachmentPublisher attachmentPublisher;

public HttpResourceValidationSteps(ResourceValidator<ResourceValidation> resourceValidator,
public HttpResourceValidationSteps(ResourceValidator<ResourceValidation> resourceValidator, ISoftAssert softAssert,
AttachmentPublisher attachmentPublisher)
{
this.resourceValidator = resourceValidator;
this.softAssert = softAssert;
this.attachmentPublisher = attachmentPublisher;
}

Expand All @@ -51,7 +54,8 @@ public HttpResourceValidationSteps(ResourceValidator<ResourceValidation> resourc
@Then("HTTP resources are valid:$resources")
public void verifyHttpResources(ExamplesTable resources)
{
List<ResourceValidation> validations = resources.getRows()
softAssert.runIgnoringTestFailFast(() -> {
List<ResourceValidation> validations = resources.getRows()
.stream()
.map(row -> row.get("url"))
.map(UriUtils::createUri)
Expand All @@ -60,7 +64,8 @@ public void verifyHttpResources(ExamplesTable resources)
.sorted()
.collect(Collectors.toList());

attachmentPublisher.publishAttachment("http-resources-validation-results.ftl", Map.of("results", validations),
"HTTP resources validation results");
attachmentPublisher.publishAttachment("http-resources-validation-results.ftl",
Map.of("results", validations), "HTTP resources validation results");
});
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,8 @@

package org.vividus.steps.http;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand All @@ -33,11 +35,14 @@
import org.vividus.http.validation.model.CheckStatus;
import org.vividus.http.validation.model.ResourceValidation;
import org.vividus.reporter.event.AttachmentPublisher;
import org.vividus.softassert.FailableRunnable;
import org.vividus.softassert.ISoftAssert;

@ExtendWith(MockitoExtension.class)
class HttpResourceValidationStepsTests
{
@Mock private ResourceValidator<ResourceValidation> resourceValidator;
@Mock private ISoftAssert softAssert;
@Mock private AttachmentPublisher attachmentPublisher;
@InjectMocks private HttpResourceValidationSteps steps;

Expand All @@ -54,6 +59,12 @@ void shouldVerifyHttpResources()
when(resourceValidator.perform(passed)).thenReturn(passed);
when(resourceValidator.perform(failed1)).thenReturn(failed1);
when(resourceValidator.perform(failed2)).thenReturn(failed2);
doAnswer(a ->
{
FailableRunnable runnable = a.getArgument(0);
runnable.run();
return null;
}).when(softAssert).runIgnoringTestFailFast(any(FailableRunnable.class));

steps.verifyHttpResources(
new ExamplesTable(String.format("|url|%n|%s|%n|%s|%n|%s|%n", passedUrl, failedUrl1, failedUrl2)));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -96,28 +96,27 @@ public void init()
* 3. If element doesn't contain href or src attribute fail assertion will be recorded
* @param cssSelector to locate resources
* @param html to validate
* @throws InterruptedException when a thread is interrupted
* @throws ExecutionException when exception thrown before result get
*/
@Then("all resources by selector `$cssSelector` from $html are valid")
public void checkResources(String cssSelector, String html) throws InterruptedException, ExecutionException
public void checkResources(String cssSelector, String html)
{
execute(() -> {
softAssert.runIgnoringTestFailFast(() -> execute(() ->
{
Collection<Element> resourcesToValidate = getElementsByCssSelector(html, cssSelector);
Stream<WebPageResourceValidation> validations = createResourceValidations(resourcesToValidate,
resourceValidation -> {
URI uriToCheck = resourceValidation.getUri();
if (isNotAbsolute(uriToCheck))
{
softAssert.recordFailedAssertion(String.format(
"Unable to resolve %s resource since the main application page URL is not set",
uriToCheck));
resourceValidation -> {
URI uriToCheck = resourceValidation.getUri();
if (isNotAbsolute(uriToCheck))
{
softAssert.recordFailedAssertion(String.format(
"Unable to resolve %s resource since the main application page URL is not set",
uriToCheck));

resourceValidation.setCheckStatus(CheckStatus.BROKEN);
}
});
resourceValidation.setCheckStatus(CheckStatus.BROKEN);
}
});
validateResources(validations);
});
}));
}

private void validateResources(Stream<WebPageResourceValidation> resourceValidation)
Expand Down Expand Up @@ -250,57 +249,65 @@ private static boolean isNotAbsolute(URI uri)
* </pre>
* @param cssSelector to locate resources
* @param pages where resources will be validated
* @throws InterruptedException when a thread is interrupted
* @throws ExecutionException when exception thrown before result get
*/
@Then("all resources by selector `$cssSelector` are valid on:$pages")
public void checkResources(String cssSelector, ExamplesTable pages) throws InterruptedException, ExecutionException
public void checkResources(String cssSelector, ExamplesTable pages)
{
execute(() -> {
Stream<WebPageResourceValidation> resourcesToValidate =
pages.getRows().stream().map(m -> m.get("pages")).parallel()
.flatMap(rawPageUrl ->
{
String pageUrl;
try
{
URI uri = resolveUri(rawPageUrl);
pageUrl = uri.toString();
if (isNotAbsolute(uri))
{
return Stream.of(createUnresolvablePageValidation(pageUrl));
}
}
catch (URISyntaxException e)
{
softAssert.recordFailedAssertion("Invalid page URL", e);
return Stream.of(createBrokenPageValidation(rawPageUrl));
}

softAssert.runIgnoringTestFailFast(() -> execute(() -> {
Stream<WebPageResourceValidation> resourcesToValidate = pages.getRows().stream().map(m -> m.get("pages"))
.parallel().flatMap(rawPageUrl ->
{
String pageUrl;
try
{
URI uri = resolveUri(rawPageUrl);
pageUrl = uri.toString();
if (isNotAbsolute(uri))
{
return Stream.of(createUnresolvablePageValidation(pageUrl));
}
}
catch (URISyntaxException e)
{
softAssert.recordFailedAssertion("Invalid page URL", e);
return Stream.of(createBrokenPageValidation(rawPageUrl));
}

try
{
httpRequestExecutor.executeHttpRequest(HttpMethod.GET, pageUrl, Optional.empty());
return Optional.ofNullable(httpTestContext.getResponse().getResponseBodyAsString())
.map(response -> getElementsByCssSelector(pageUrl, response, cssSelector))
.map(elements -> createResourceValidations(elements,
rV -> rV.setPageURL(pageUrl)
))
.orElseGet(() -> Stream.of(createMissingPageBodyValidation(pageUrl)));
}
catch (IOException toReport)
{
return Stream.of(createUnreachablePageValidation(pageUrl, toReport));
}
});
try
{
httpRequestExecutor.executeHttpRequest(HttpMethod.GET, pageUrl, Optional.empty());
return Optional.ofNullable(httpTestContext.getResponse().getResponseBodyAsString())
.map(response -> getElementsByCssSelector(pageUrl, response, cssSelector))
.map(elements -> createResourceValidations(elements,
rV -> rV.setPageURL(pageUrl)
))
.orElseGet(() -> Stream.of(createMissingPageBodyValidation(pageUrl)));
}
catch (IOException toReport)
{
return Stream.of(createUnreachablePageValidation(pageUrl, toReport));
}
});
validateResources(resourcesToValidate);
});
}));
}

private void execute(Runnable executable) throws InterruptedException, ExecutionException
private void execute(Runnable executable)
{
executor.execute(executable,
(t, e) -> softAssert.recordFailedAssertion("Exception occured in thread with name: " + t.getName(), e));
try
{
executor.execute(executable, (t, e) -> softAssert
.recordFailedAssertion("Exception occured in thread with name: " + t.getName(), e));
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
catch (ExecutionException e)
{
throw new IllegalStateException(e);
}
}

private WebPageResourceValidation createUnreachablePageValidation(String pageURL, Exception exception)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,10 +21,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
Expand All @@ -46,6 +48,7 @@

import org.jbehave.core.model.ExamplesTable;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -61,6 +64,7 @@
import org.vividus.http.validation.ResourceValidator;
import org.vividus.http.validation.model.CheckStatus;
import org.vividus.reporter.event.AttachmentPublisher;
import org.vividus.softassert.FailableRunnable;
import org.vividus.softassert.ISoftAssert;
import org.vividus.testcontext.ContextCopyingExecutor;
import org.vividus.ui.web.configuration.WebApplicationConfiguration;
Expand Down Expand Up @@ -172,6 +176,17 @@ class ResourceCheckStepsTests
@InjectMocks
private ResourceCheckSteps resourceCheckSteps;

@BeforeEach
void beforeEach()
{
doAnswer(a ->
{
FailableRunnable runnable = a.getArgument(0);
runnable.run();
return null;
}).when(softAssert).runIgnoringTestFailFast(any(FailableRunnable.class));
}

@Test
void shouldCheckDesiredResourcesAndPostAttachment() throws InterruptedException, ExecutionException
{
Expand Down Expand Up @@ -410,6 +425,25 @@ void shouldReportExceptionOccurredInExecutingThread() throws InterruptedExceptio
verifyNoInteractions(httpTestContext, attachmentPublisher, resourceValidator);
}

static Stream<Arguments> exceptions()
{
return Stream.of(arguments(new InterruptedException()), arguments(new ExecutionException(new IOException())));
}

@ParameterizedTest
@MethodSource("exceptions")
void shouldWrapExceptionsOccurredInExecutingThread(Exception exception)
throws ExecutionException, InterruptedException
{
doAnswer(a ->
{
throw exception;
}).when(executor).execute(any(), any());
var illegalStateException = assertThrows(IllegalStateException.class,
() -> resourceCheckSteps.checkResources(LINK_SELECTOR, FIRST_PAGE_TABLE));
assertEquals(exception, illegalStateException.getCause());
}

private void mockResourceValidator()
{
when(resourceValidator.perform(any(WebPageResourceValidation.class)))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,4 +19,8 @@
public interface FailTestFastManager
{
boolean isFailTestCaseFast();

void enableTestCaseFailFast();

void disableTestCaseFailFast();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vividus.softassert;

@FunctionalInterface
public interface FailableRunnable<E extends Exception>
{
void run() throws E;
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,13 @@ public interface ISoftAssert
* @throws VerificationError If there were any assertion errors collected
*/
void verify(Pattern pattern) throws VerificationError;

/**
* Disable fail-fast at the test case level and execute <code>FailableRunnable</code> operation.
* After the operation completed fail-fast setting is restored and error verification is performed.
* @param <E> Throws if an exception occurred during execution of the operation
* @param runnable FailableRunnable operation
* @throws E If an exception occurred during execution of the operation
*/
<E extends Exception> void runIgnoringTestFailFast(FailableRunnable<E> runnable) throws E;
}
Loading
0