Skip to content

Commit

Permalink
Basic integration tests for hooks framework (#1250)
Browse files Browse the repository at this point in the history
* Sample module for integration testing

* Integration test specifically for hooks invocation

* Add sample raw auction request hook to integration test

* Add sample processed auction request hook to integration test

* Add sample bidder request hook to integration test

* Add sample raw bidder response hook to integration test

* Add sample processed bidder response hook to integration test

* Add sample auction response hook to integration test

* Simplify integration test for hooks framework by using a separate account

* Add tests for rejection scenarios at different stages
  • Loading branch information
schernysh authored May 11, 2021
1 parent 3590d50 commit 24c9176
Show file tree
Hide file tree
Showing 37 changed files with 1,578 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,12 @@ private ExtRequest extRequest(BidRequest bidRequest,
prebidBuilder.amp(ExtRequestPrebidAmp.of(updatedAmpData));
}

result = ExtRequest.of(prebidBuilder.build());
final ExtRequest updatedExt = ExtRequest.of(prebidBuilder.build());
if (requestExt != null) {
updatedExt.addProperties(requestExt.getProperties());
}

result = updatedExt;
} else {
result = bidRequest.getExt();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,14 +493,17 @@ private ExtRequest populateRequestExt(ExtRequest ext, BidRequest bidRequest, Lis
? prebid.toBuilder()
: ExtRequestPrebid.builder();

return ExtRequest.of(prebidBuilder
final ExtRequest updatedExt = ExtRequest.of(prebidBuilder
.targeting(ObjectUtils.defaultIfNull(updatedTargeting,
getIfNotNull(prebid, ExtRequestPrebid::getTargeting)))
.cache(ObjectUtils.defaultIfNull(updatedCache,
getIfNotNull(prebid, ExtRequestPrebid::getCache)))
.channel(ObjectUtils.defaultIfNull(updatedChannel,
getIfNotNull(prebid, ExtRequestPrebid::getChannel)))
.build());
updatedExt.addProperties(ext.getProperties());

return updatedExt;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ private Future<Account> accountFrom(BidRequest bidRequest, Timeout timeout, Http
return isAccountIdBlank
? responseForEmptyAccount(httpRequest)
: applicationSettings.getAccountById(accountId, timeout)
.compose(this::ensureAccountActive,
exception -> accountFallback(exception, accountId, httpRequest));
.compose(this::ensureAccountActive,
exception -> accountFallback(exception, accountId, httpRequest));
}

/**
Expand Down Expand Up @@ -312,7 +312,12 @@ private ExtRequest enrichExtRequest(ExtRequest ext, Account account) {

prebidExtBuilder.integration(accountDefaultIntegration);

return ExtRequest.of(prebidExtBuilder.build());
final ExtRequest updatedExt = ExtRequest.of(prebidExtBuilder.build());
if (ext != null) {
updatedExt.addProperties(ext.getProperties());
}

return updatedExt;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import lombok.Builder;
import lombok.Value;
import lombok.experimental.Accessors;
import lombok.experimental.NonFinal;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.api.Assertions;
Expand Down Expand Up @@ -54,7 +52,6 @@
import org.prebid.server.hooks.v1.InvocationContext;
import org.prebid.server.hooks.v1.InvocationResult;
import org.prebid.server.hooks.v1.InvocationStatus;
import org.prebid.server.hooks.v1.PayloadUpdate;
import org.prebid.server.hooks.v1.auction.AuctionInvocationContext;
import org.prebid.server.hooks.v1.auction.AuctionRequestPayload;
import org.prebid.server.hooks.v1.auction.AuctionResponseHook;
Expand Down Expand Up @@ -2550,56 +2547,4 @@ public String code() {
return code;
}
}

@Accessors(fluent = true)
@Builder
@Value
private static class InvocationResultImpl<PAYLOAD> implements InvocationResult<PAYLOAD> {

InvocationStatus status;

String message;

InvocationAction action;

PayloadUpdate<PAYLOAD> payloadUpdate;

List<String> errors;

List<String> warnings;

List<String> debugMessages;

Object moduleContext;

public static <PAYLOAD> InvocationResult<PAYLOAD> succeeded(PayloadUpdate<PAYLOAD> payloadUpdate) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.update)
.payloadUpdate(payloadUpdate)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> failed(String message) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.failure)
.message(message)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> noAction() {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.no_action)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> rejected(String message) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.reject)
.message(message)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.prebid.server.hooks.execution;

import lombok.Builder;
import lombok.Value;
import lombok.experimental.Accessors;
import org.prebid.server.hooks.v1.InvocationAction;
import org.prebid.server.hooks.v1.InvocationResult;
import org.prebid.server.hooks.v1.InvocationStatus;
import org.prebid.server.hooks.v1.PayloadUpdate;

import java.util.List;

@Accessors(fluent = true)
@Builder
@Value
public class InvocationResultImpl<PAYLOAD> implements InvocationResult<PAYLOAD> {

InvocationStatus status;

String message;

InvocationAction action;

PayloadUpdate<PAYLOAD> payloadUpdate;

List<String> errors;

List<String> warnings;

List<String> debugMessages;

Object moduleContext;

public static <PAYLOAD> InvocationResult<PAYLOAD> succeeded(PayloadUpdate<PAYLOAD> payloadUpdate) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.update)
.payloadUpdate(payloadUpdate)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> failed(String message) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.failure)
.message(message)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> noAction() {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.no_action)
.build();
}

public static <PAYLOAD> InvocationResult<PAYLOAD> rejected(String message) {
return InvocationResultImpl.<PAYLOAD>builder()
.status(InvocationStatus.success)
.action(InvocationAction.reject)
.message(message)
.build();
}
}
11 changes: 7 additions & 4 deletions src/test/java/org/prebid/server/it/IntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.prebid.server.cache.proto.request.PutObject;
import org.prebid.server.cache.proto.response.BidCacheResponse;
import org.prebid.server.cache.proto.response.CacheObject;
import org.prebid.server.it.hooks.TestHooksConfiguration;
import org.prebid.server.it.util.BidCacheRequestPattern;
import org.skyscreamer.jsonassert.ArrayValueMatcher;
import org.skyscreamer.jsonassert.Customization;
Expand All @@ -29,6 +30,7 @@
import org.skyscreamer.jsonassert.ValueMatcher;
import org.skyscreamer.jsonassert.comparator.CustomComparator;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;

import java.io.IOException;
Expand All @@ -42,7 +44,8 @@
import static java.lang.String.format;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@TestPropertySource("test-application.properties")
@TestPropertySource({"test-application.properties", "test-application-hooks.properties"})
@Import(TestHooksConfiguration.class)
public abstract class IntegrationTest extends VertxTest {

private static final int APP_PORT = 8080;
Expand All @@ -59,7 +62,7 @@ public abstract class IntegrationTest extends VertxTest {
@Rule
public WireMockClassRule instanceRule = WIRE_MOCK_RULE;

static final RequestSpecification SPEC = spec(APP_PORT);
protected static final RequestSpecification SPEC = spec(APP_PORT);

@BeforeClass
public static void setUp() throws IOException {
Expand All @@ -78,12 +81,12 @@ static RequestSpecification spec(int port) {
.build();
}

static String jsonFrom(String file) throws IOException {
protected static String jsonFrom(String file) throws IOException {
// workaround to clear formatting
return mapper.writeValueAsString(mapper.readTree(IntegrationTest.class.getResourceAsStream(file)));
}

static String openrtbAuctionResponseFrom(String templatePath, Response response, List<String> bidders)
protected static String openrtbAuctionResponseFrom(String templatePath, Response response, List<String> bidders)
throws IOException {

return auctionResponseFrom(templatePath, response, "ext.responsetimemillis.%s", bidders);
Expand Down
147 changes: 147 additions & 0 deletions src/test/java/org/prebid/server/it/hooks/HooksTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package org.prebid.server.it.hooks;

import io.restassured.response.Response;
import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.prebid.server.it.IntegrationTest;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static io.restassured.RestAssured.given;
import static java.util.Collections.singletonList;
import static org.hamcrest.Matchers.empty;

@RunWith(SpringRunner.class)
public class HooksTest extends IntegrationTest {

private static final String RUBICON = "rubicon";

@Test
public void openrtb2AuctionShouldRunHooksAtEachStage() throws IOException, JSONException {
// given
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/rubicon-exchange"))
.withRequestBody(equalToJson(jsonFrom("hooks/sample-module/test-rubicon-bid-request-1.json")))
.willReturn(aResponse().withBody(jsonFrom("hooks/sample-module/test-rubicon-bid-response-1.json"))));

// when
final Response response = given(SPEC)
.queryParam("sample-it-module-update", "headers,body")
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/sample-module/test-auction-sample-module-request.json"))
.post("/openrtb2/auction");

// then
final String expectedAuctionResponse = openrtbAuctionResponseFrom(
"hooks/sample-module/test-auction-sample-module-response.json", response, singletonList(RUBICON));

JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.LENIENT);
}

@Test
public void openrtb2AuctionShouldBeRejectedByEntrypointHook() throws IOException {
given(SPEC)
.queryParam("sample-it-module-reject", "true")
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/sample-module/test-auction-sample-module-request.json"))
.post("/openrtb2/auction")
.then()
.statusCode(200)
.body("seatbid", empty());
}

@Test
public void openrtb2AuctionShouldBeRejectedByRawAuctionRequestHook() throws IOException {
given(SPEC)
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/reject/test-auction-raw-auction-request-reject-request.json"))
.post("/openrtb2/auction")
.then()
.statusCode(200)
.body("seatbid", empty());
}

@Test
public void openrtb2AuctionShouldBeRejectedByProcessedAuctionRequestHook() throws IOException {
given(SPEC)
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/reject/test-auction-processed-auction-request-reject-request.json"))
.post("/openrtb2/auction")
.then()
.statusCode(200)
.body("seatbid", empty());
}

@Test
public void openrtb2AuctionShouldRejectRubiconBidderByBidderRequestHook() throws IOException, JSONException {
// when
final Response response = given(SPEC)
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/reject/test-auction-bidder-request-reject-request.json"))
.post("/openrtb2/auction");

// then
final String expectedAuctionResponse = openrtbAuctionResponseFrom(
"hooks/reject/test-auction-bidder-request-reject-response.json", response, singletonList(RUBICON));

JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.LENIENT);

WIRE_MOCK_RULE.verify(0, postRequestedFor(urlPathEqualTo("/rubicon-exchange")));
}

@Test
public void openrtb2AuctionShouldRejectRubiconBidderByRawBidderResponseHook() throws IOException, JSONException {
// given
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/rubicon-exchange"))
.willReturn(aResponse().withBody(jsonFrom("hooks/reject/test-rubicon-bid-response-1.json"))));

// when
final Response response = given(SPEC)
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/reject/test-auction-raw-bidder-response-reject-request.json"))
.post("/openrtb2/auction");

// then
final String expectedAuctionResponse = openrtbAuctionResponseFrom(
"hooks/reject/test-auction-raw-bidder-response-reject-response.json", response, singletonList(RUBICON));

JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.LENIENT);

WIRE_MOCK_RULE.verify(1, postRequestedFor(urlPathEqualTo("/rubicon-exchange"))
.withRequestBody(equalToJson(jsonFrom("hooks/reject/test-rubicon-bid-request-1.json"))));
}

@Test
public void openrtb2AuctionShouldRejectRubiconBidderByProcessedBidderResponseHook()
throws IOException, JSONException {

// given
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/rubicon-exchange"))
.willReturn(aResponse().withBody(jsonFrom("hooks/reject/test-rubicon-bid-response-1.json"))));

// when
final Response response = given(SPEC)
.header("User-Agent", "userAgent")
.body(jsonFrom("hooks/reject/test-auction-processed-bidder-response-reject-request.json"))
.post("/openrtb2/auction");

// then
final String expectedAuctionResponse = openrtbAuctionResponseFrom(
"hooks/reject/test-auction-processed-bidder-response-reject-response.json",
response,
singletonList(RUBICON));

JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.LENIENT);

WIRE_MOCK_RULE.verify(1, postRequestedFor(urlPathEqualTo("/rubicon-exchange"))
.withRequestBody(equalToJson(jsonFrom("hooks/reject/test-rubicon-bid-request-1.json"))));
}
}
Loading

0 comments on commit 24c9176

Please sign in to comment.