diff --git a/src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java b/src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java index 6beba78b3dc..42c12a48d1c 100644 --- a/src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java +++ b/src/main/java/org/prebid/server/bidder/sparteo/SparteoBidder.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Publisher; @@ -12,12 +13,14 @@ import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderCall; import org.prebid.server.bidder.model.BidderError; import org.prebid.server.bidder.model.HttpRequest; import org.prebid.server.bidder.model.Result; +import org.prebid.server.bidder.sparteo.util.SparteoUtil; import org.prebid.server.exception.PreBidException; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; @@ -28,7 +31,9 @@ import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; +import org.apache.http.client.utils.URIBuilder; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -38,6 +43,8 @@ public class SparteoBidder implements Bidder { + private static final String UNKNOWN_VALUE = "unknown"; + private static final TypeReference> TYPE_REFERENCE = new TypeReference<>() { }; @@ -53,19 +60,18 @@ public SparteoBidder(String endpointUrl, JacksonMapper mapper) { public Result>> makeHttpRequests(BidRequest request) { final List errors = new ArrayList<>(); final List modifiedImps = new ArrayList<>(); - String siteNetworkId = null; + String networkId = null; for (Imp imp : request.getImp()) { - if (siteNetworkId == null) { + if (networkId == null) { try { - siteNetworkId = parseExtImp(imp).getNetworkId(); + networkId = parseExtImp(imp).getNetworkId(); } catch (PreBidException e) { errors.add(BidderError.badInput( "ignoring imp id=%s, error processing ext: %s".formatted( imp.getId(), e.getMessage()))); } } - final ObjectNode modifiedExt = modifyImpExt(imp); modifiedImps.add(imp.toBuilder().ext(modifiedExt).build()); } @@ -74,12 +80,17 @@ public Result>> makeHttpRequests(BidRequest request return Result.withErrors(errors); } + final Site site = request.getSite(); + final App app = request.getApp(); + final BidRequest outgoingRequest = request.toBuilder() .imp(modifiedImps) - .site(modifySite(request.getSite(), siteNetworkId, mapper)) + .site(modifySite(site, networkId)) + .app(modifyApp(app, networkId)) .build(); - final HttpRequest call = BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper); + final String finalEndpointUrl = replaceMacros(site, app, networkId, errors); + final HttpRequest call = BidderUtil.defaultRequest(outgoingRequest, finalEndpointUrl, mapper); return Result.of(Collections.singletonList(call), errors); } @@ -97,45 +108,138 @@ private static ObjectNode modifyImpExt(Imp imp) { final ObjectNode sparteoNode = modifiedImpExt.putObject("sparteo"); final JsonNode bidderJsonNode = modifiedImpExt.remove("bidder"); sparteoNode.set("params", bidderJsonNode); - return modifiedImpExt; } - private Site modifySite(Site site, String siteNetworkId, JacksonMapper mapper) { - if (site == null || site.getPublisher() == null || siteNetworkId == null) { + private Site modifySite(Site site, String networkId) { + if (site == null) { return site; } - final Publisher originalPublisher = site.getPublisher(); - final ExtPublisher originalExt = originalPublisher.getExt(); + final Publisher originalPublisher = site.getPublisher() != null + ? site.getPublisher() + : Publisher.builder().build(); + + final Publisher modifiedPublisher = modifyPublisher(originalPublisher, networkId); - final ExtPublisher modifiedExt = originalExt != null - ? ExtPublisher.of(originalExt.getPrebid()) - : ExtPublisher.empty(); + return site.toBuilder().publisher(modifiedPublisher).build(); + } - if (originalExt != null) { - mapper.fillExtension(modifiedExt, originalExt); + private App modifyApp(App app, String networkId) { + if (app == null) { + return app; } - final JsonNode paramsProperty = modifiedExt.getProperty("params"); - final ObjectNode paramsNode; + final Publisher originalPublisher = app.getPublisher() != null + ? app.getPublisher() + : Publisher.builder().build(); - if (paramsProperty != null && paramsProperty.isObject()) { - paramsNode = (ObjectNode) paramsProperty; - } else { - paramsNode = mapper.mapper().createObjectNode(); - modifiedExt.addProperty("params", paramsNode); - } + final Publisher modifiedPublisher = modifyPublisher(originalPublisher, networkId); + + return app.toBuilder().publisher(modifiedPublisher).build(); + } + + private Publisher modifyPublisher(Publisher originalPublisher, String networkId) { + final ExtPublisher originalExt = originalPublisher.getExt(); + final ExtPublisher modifiedExt = originalExt == null + ? ExtPublisher.empty() + : mapper.mapper().convertValue(originalExt, ExtPublisher.class); - paramsNode.put("networkId", siteNetworkId); + final ObjectNode paramsNode = ensureParamsNode(modifiedExt); + paramsNode.put("networkId", networkId); - final Publisher modifiedPublisher = originalPublisher.toBuilder() + return originalPublisher.toBuilder() .ext(modifiedExt) .build(); + } - return site.toBuilder() - .publisher(modifiedPublisher) - .build(); + private ObjectNode ensureParamsNode(ExtPublisher extPublisher) { + final JsonNode paramsProperty = extPublisher.getProperty("params"); + if (paramsProperty != null && paramsProperty.isObject()) { + return (ObjectNode) paramsProperty; + } + final ObjectNode paramsNode = mapper.mapper().createObjectNode(); + extPublisher.addProperty("params", paramsNode); + + return paramsNode; + } + + private String replaceMacros(Site site, App app, String networkId, List errors) { + final String siteDomain = resolveSiteDomain(site); + final String appDomain = resolveAppDomain(app); + final String bundle = resolveBundle(app); + + if (UNKNOWN_VALUE.equals(siteDomain)) { + errors.add(BidderError.badInput( + "Domain not found. Missing the site.domain or the site.page field")); + } + + if (UNKNOWN_VALUE.equals(bundle)) { + errors.add(BidderError.badInput( + "Bundle not found. Missing the app.bundle field.")); + } + + return resolveEndpoint(siteDomain, appDomain, networkId, bundle); + } + + private String resolveSiteDomain(Site site) { + if (site == null) { + return null; + } + + return Optional.of(site) + .map(Site::getDomain) + .map(SparteoUtil::normalizeHostname) + .filter(StringUtils::isNotEmpty) + .or(() -> Optional.ofNullable(site.getPage()) + .map(SparteoUtil::normalizeHostname) + .filter(StringUtils::isNotEmpty)) + .orElse(UNKNOWN_VALUE); + } + + private String resolveAppDomain(App app) { + if (app == null) { + return null; + } + + return Optional.of(app) + .map(App::getDomain) + .map(SparteoUtil::normalizeHostname) + .filter(StringUtils::isNotEmpty) + .orElse(UNKNOWN_VALUE); + } + + private String resolveBundle(App app) { + if (app == null) { + return null; + } + + return Optional.ofNullable(app.getBundle()) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .filter(bundle -> !"null".equalsIgnoreCase(bundle)) + .orElse(UNKNOWN_VALUE); + } + + private String resolveEndpoint(String siteDomain, String appDomain, String networkId, String bundle) { + try { + final URIBuilder uriBuilder = new URIBuilder(endpointUrl); + if (StringUtils.isNotBlank(networkId)) { + uriBuilder.addParameter("network_id", networkId); + } + if (StringUtils.isNotBlank(siteDomain)) { + uriBuilder.addParameter("site_domain", siteDomain); + } + if (StringUtils.isNotBlank(appDomain)) { + uriBuilder.addParameter("app_domain", appDomain); + } + if (StringUtils.isNotBlank(bundle)) { + uriBuilder.addParameter("bundle", bundle); + } + return uriBuilder.build().toString(); + } catch (URISyntaxException e) { + throw new PreBidException("Failed to build endpoint URL", e); + } } @Override diff --git a/src/main/java/org/prebid/server/bidder/sparteo/util/SparteoUtil.java b/src/main/java/org/prebid/server/bidder/sparteo/util/SparteoUtil.java new file mode 100644 index 00000000000..bcb098f687d --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/sparteo/util/SparteoUtil.java @@ -0,0 +1,41 @@ +package org.prebid.server.bidder.sparteo.util; + +import org.apache.commons.lang3.StringUtils; + +import java.net.URI; +import java.net.URISyntaxException; + +public final class SparteoUtil { + + private SparteoUtil() { + } + + public static String normalizeHostname(String host) { + String h = StringUtils.trimToEmpty(host); + if (h.isEmpty()) { + return ""; + } + + String hostname = null; + try { + hostname = new URI(h).getHost(); + } catch (URISyntaxException e) { + } + + if (StringUtils.isNotEmpty(hostname)) { + h = hostname; + } else { + if (h.contains(":")) { + h = StringUtils.substringBefore(h, ":"); + } else { + h = StringUtils.substringBefore(h, "/"); + } + } + + h = h.toLowerCase(); + h = StringUtils.removeStart(h, "www."); + h = StringUtils.removeEnd(h, "."); + + return "null".equals(h) ? "" : h; + } +} diff --git a/src/main/resources/bidder-config/sparteo.yaml b/src/main/resources/bidder-config/sparteo.yaml index 44cbe16bd86..5dd9dd19b1d 100644 --- a/src/main/resources/bidder-config/sparteo.yaml +++ b/src/main/resources/bidder-config/sparteo.yaml @@ -16,5 +16,5 @@ adapters: usersync: cookie-family-name: sparteo iframe: - url: "https://sync.sparteo.com/s2s_sync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect_url={{redirect_url}}" + url: "https://sync.sparteo.com/s2s_sync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&gpp={{gpp}}&&gpp_sid={{gpp_sid}}&redirect_url={{redirect_url}}" support-cors: true diff --git a/src/test/java/org/prebid/server/bidder/sparteo/SparteoBidderTest.java b/src/test/java/org/prebid/server/bidder/sparteo/SparteoBidderTest.java index 2920f94adbd..e741c5e29c6 100644 --- a/src/test/java/org/prebid/server/bidder/sparteo/SparteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/sparteo/SparteoBidderTest.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Publisher; @@ -20,7 +21,6 @@ import org.prebid.server.bidder.model.HttpRequest; import org.prebid.server.bidder.model.HttpResponse; import org.prebid.server.bidder.model.Result; -import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtPublisher; import org.prebid.server.proto.openrtb.ext.response.BidType; @@ -44,62 +44,13 @@ public class SparteoBidderTest extends VertxTest { @Test public void creationShouldFailOnInvalidEndpointUrl() { - // when and then - assertThatIllegalArgumentException() - .isThrownBy(() -> new SparteoBidder("invalid_url", jacksonMapper)) - .withMessage("URL supplied is not valid: invalid_url"); - } - - @Test - public void makeHttpRequestsShouldReturnErrorWhenImpExtIsInvalid() { // given - final BidRequest bidRequest = givenBidRequest( - givenImp(imp -> imp.ext(mapper.valueToTree(ExtPrebid.of(null, "invalid"))))); - - // when - final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).hasSize(1) - .allSatisfy(error -> assertThat(error.getMessage()) - .startsWith("ignoring imp id=impId, error processing ext: invalid imp.ext")); - } - - @Test - public void makeHttpRequestsShouldReturnErrorWhenAllImpsAreInvalid() { - // given - final BidRequest bidRequest = givenBidRequest( - givenImp(imp -> imp.id("imp1").ext(mapper.valueToTree(ExtPrebid.of(null, "invalid"))))); - - // when - final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).hasSize(1); - } - - @Test - public void makeHttpRequestsShouldReturnPartialResultWhenSomeImpsAreInvalid() { - // given - final ObjectNode validExt = mapper.createObjectNode(); - validExt.set("bidder", mapper.createObjectNode().put("key", "value")); - - final BidRequest bidRequest = givenBidRequest( - givenImp(imp -> imp.id("imp1").ext(mapper.valueToTree(ExtPrebid.of(null, "invalid")))), - givenImp(imp -> imp.id("imp2").ext(validExt))); - - // when - final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + final String invalidUrl = "invalid_url"; - // then - assertThat(result.getErrors()).hasSize(1) - .allSatisfy(error -> assertThat(error.getMessage()).startsWith("ignoring imp id=imp1")); - assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .flatExtracting(BidRequest::getImp) - .extracting(Imp::getExt) - .extracting((JsonNode ext) -> ext.at("/sparteo/params/key").asText()) - .containsExactly("", "value"); + // when and then + assertThatIllegalArgumentException() + .isThrownBy(() -> new SparteoBidder(invalidUrl, jacksonMapper)) + .withMessage("URL supplied is not valid: " + invalidUrl); } @Test @@ -118,10 +69,11 @@ public void makeHttpRequestsShouldSetNetworkIdOnSitePublisherExtWhenPresentInImp final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).isEmpty(); + assertMissingSiteDomainWarning(result); assertThat(result.getValue()) .extracting(HttpRequest::getMethod, HttpRequest::getUri) - .containsExactly(tuple(HttpMethod.POST, ENDPOINT_URL)); + .containsExactly(tuple(HttpMethod.POST, + "https://test.sparteo.com/endpoint?network_id=testNetworkId&site_domain=unknown")); assertThat(result.getValue()) .extracting(HttpRequest::getPayload) @@ -138,7 +90,7 @@ public void makeHttpRequestsShouldSetNetworkIdOnSitePublisherExtWhenPresentInImp .extracting(BidRequest::getSite) .extracting(Site::getPublisher) .extracting(Publisher::getExt) - .extracting(ext -> ((ExtPublisher) ext).getProperties().get("params").get("networkId").asText()) + .extracting(ext -> ext.getProperties().get("params").get("networkId").asText()) .containsExactly("testNetworkId"); } @@ -159,21 +111,25 @@ public void makeHttpRequestsShouldUseFirstNetworkIdWhenMultipleImpsDefineIt() { final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).isEmpty(); + assertMissingSiteDomainWarning(result); assertThat(result.getValue()) .extracting(HttpRequest::getPayload) .extracting(BidRequest::getSite) .extracting(Site::getPublisher) .extracting(Publisher::getExt) - .extracting(ext -> ((ExtPublisher) ext).getProperties().get("params").get("networkId").asText()) + .extracting(ext -> ext.getProperties().get("params").get("networkId").asText()) .containsExactly("id1"); } @Test - public void makeHttpRequestsShouldOverwriteSparteoParamsWithBidderParamsOnConflict() { + public void makeHttpRequestsShouldMergeSparteoParamsWithBidderParamsOnConflict() { // given + final ObjectNode bidderNode = mapper.createObjectNode(); + bidderNode.put("conflictingParam", "bidderValue"); + bidderNode.put("networkId", "id1"); + final ObjectNode impExt = mapper.createObjectNode(); - impExt.set("bidder", mapper.createObjectNode().put("conflictingParam", "bidderValue")); + impExt.set("bidder", bidderNode); final ObjectNode sparteoNode = impExt.putObject("sparteo"); sparteoNode.putObject("params").put("conflictingParam", "sparteoValue"); @@ -190,6 +146,12 @@ public void makeHttpRequestsShouldOverwriteSparteoParamsWithBidderParamsOnConfli .extracting(Imp::getExt) .extracting((JsonNode ext) -> ext.at("/sparteo/params/conflictingParam").asText()) .containsExactly("bidderValue"); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getExt) + .extracting((JsonNode ext) -> ext.at("/sparteo/params/networkId").asText()) + .containsExactly("id1"); } @Test @@ -198,7 +160,8 @@ public void makeHttpRequestsShouldHandleRequestWithoutSiteOrPublisher() { final ObjectNode impExt = mapper.createObjectNode(); impExt.set("bidder", mapper.createObjectNode().put("networkId", "testNetworkId")); - final BidRequest bidRequestNoSite = givenBidRequest(request -> request.site(null), + final BidRequest bidRequestNoSite = givenBidRequest( + request -> request.site(null), givenImp(imp -> imp.ext(impExt))); final BidRequest bidRequestNoPublisher = givenBidRequest( @@ -211,18 +174,19 @@ public void makeHttpRequestsShouldHandleRequestWithoutSiteOrPublisher() { sparteoBidder.makeHttpRequests(bidRequestNoPublisher); // then - assertThat(resultNoSite.getErrors()).isEmpty(); assertThat(resultNoSite.getValue()) .extracting(HttpRequest::getPayload) .extracting(BidRequest::getSite) .containsNull(); - assertThat(resultNoPublisher.getErrors()).isEmpty(); + assertThat(resultNoSite.getErrors()).isEmpty(); assertThat(resultNoPublisher.getValue()) .extracting(HttpRequest::getPayload) .extracting(BidRequest::getSite) .extracting(Site::getPublisher) - .containsNull(); + .extracting(Publisher::getExt) + .extracting(ext -> ext.getProperties().get("params").get("networkId").asText()) + .containsExactly("testNetworkId"); } @Test @@ -244,13 +208,13 @@ public void makeHttpRequestsShouldMergeNetworkIdIntoExistingPublisherExtParams() final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).isEmpty(); + assertMissingSiteDomainWarning(result); assertThat(result.getValue()) .extracting(HttpRequest::getPayload) .extracting(BidRequest::getSite) .extracting(Site::getPublisher) .extracting(Publisher::getExt) - .extracting(ext -> ((ExtPublisher) ext).getProperties().get("params")) + .extracting(ext -> ext.getProperties().get("params")) .allSatisfy(params -> { assertThat(params.get("networkId").asText()).isEqualTo("testNetworkId"); assertThat(params.get("existingParam").asText()).isEqualTo("existingValue"); @@ -259,7 +223,7 @@ public void makeHttpRequestsShouldMergeNetworkIdIntoExistingPublisherExtParams() @Test public void makeHttpRequestsShouldAddParamsToPublisherExtWhenExtExistsWithoutParams() - throws JsonProcessingException { + throws JsonProcessingException { // given final ObjectNode impExt = mapper.createObjectNode(); impExt.set("bidder", mapper.createObjectNode().put("networkId", "testNetworkId")); @@ -277,13 +241,13 @@ public void makeHttpRequestsShouldAddParamsToPublisherExtWhenExtExistsWithoutPar final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).isEmpty(); + assertMissingSiteDomainWarning(result); assertThat(result.getValue()) .extracting(HttpRequest::getPayload) .extracting(BidRequest::getSite) .extracting(Site::getPublisher) .extracting(Publisher::getExt) - .extracting(ext -> ((ExtPublisher) ext).getProperties()) + .extracting(ext -> ext.getProperties()) .allSatisfy(properties -> { assertThat(properties.get("params").get("networkId").asText()).isEqualTo("testNetworkId"); assertThat(properties.get("otherField").asText()).isEqualTo("otherValue"); @@ -291,38 +255,55 @@ public void makeHttpRequestsShouldAddParamsToPublisherExtWhenExtExistsWithoutPar } @Test - public void makeHttpRequestsShouldCreateEmptyParamsWhenBidderExtIsEmpty() { + public void makeHttpRequestsShouldReturnEmptyResultWhenRequestHasNoImps() { + // given + final BidRequest bidRequest = givenBidRequest(request -> request.imp(Collections.emptyList())); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldAppendSiteDomainAndNetworkIdAsQueryParams() { // given final ObjectNode impExt = mapper.createObjectNode(); - impExt.set("bidder", mapper.createObjectNode()); - impExt.putObject("sparteo"); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "testNetworkId")); - final BidRequest bidRequest = givenBidRequest(givenImp(imp -> imp.ext(impExt))); + final BidRequest bidRequest = givenBidRequest( + r -> r.site(Site.builder() + .domain("dev.sparteo.com") + .publisher(Publisher.builder().build()) + .build()), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1); assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .flatExtracting(BidRequest::getImp) - .extracting(Imp::getExt) - .allSatisfy(ext -> { - assertThat(ext.at("/sparteo")).isInstanceOf(ObjectNode.class); - assertThat(ext.at("/sparteo/params")).isInstanceOf(ObjectNode.class); - assertThat(ext.at("/sparteo/params").size()).isZero(); - }); + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=testNetworkId&site_domain=dev.sparteo.com"); } @Test - public void makeHttpRequestsShouldNotAddParamsWhenNetworkIdIsMissing() { + public void makeHttpRequestsShouldAppendSitePageDomainAndNetworkIdAsQueryParams() { // given final ObjectNode impExt = mapper.createObjectNode(); - impExt.set("bidder", mapper.createObjectNode().put("otherParam", "value")); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "testNetworkId")); + final BidRequest bidRequest = givenBidRequest( - request -> request.site(Site.builder().publisher(Publisher.builder().id("pub1").build()).build()), - givenImp(imp -> imp.ext(impExt))); + r -> r.site(Site.builder() + .domain(null) + .page("https://www.dev.sparteo.com:3000/p") + .publisher(Publisher.builder().build()) + .build()), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); @@ -330,30 +311,45 @@ public void makeHttpRequestsShouldNotAddParamsWhenNetworkIdIsMissing() { // then assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .extracting(BidRequest::getSite) - .extracting(Site::getPublisher) - .allSatisfy(publisher -> { - final ExtPublisher publisherExt = (ExtPublisher) publisher.getExt(); - if (publisherExt == null || publisherExt.getProperties() == null) { - assertThat(publisherExt).isNull(); - } else { - assertThat(publisherExt.getProperties()).doesNotContainKey("params"); - } - }); + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=testNetworkId&site_domain=dev.sparteo.com"); } @Test - public void makeHttpRequestsShouldNotAddNetworkIdWhenItIsNullInExt() { + public void makeHttpRequestsShouldUseSiteDomainWhenPublisherDomainIsMissing() { // given final ObjectNode impExt = mapper.createObjectNode(); - final ObjectNode bidderNode = mapper.createObjectNode(); - bidderNode.putNull("networkId"); - impExt.set("bidder", bidderNode); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "testNetworkId")); final BidRequest bidRequest = givenBidRequest( - request -> request.site(Site.builder().publisher(Publisher.builder().id("pub1").build()).build()), - givenImp(imp -> imp.ext(impExt))); + r -> r.site(Site.builder() + .domain("dev.sparteo.com") + .publisher(Publisher.builder().build()) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=testNetworkId&site_domain=dev.sparteo.com"); + } + + @Test + public void makeHttpRequestsShouldPreferSiteDomainOverPublisherDomain() { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); + + final BidRequest bidRequest = givenBidRequest( + r -> r.site(Site.builder() + .domain("site.sparteo.com") + .publisher(Publisher.builder().domain("dev.sparteo.com").build()) + .build()), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); @@ -361,34 +357,110 @@ public void makeHttpRequestsShouldNotAddNetworkIdWhenItIsNullInExt() { // then assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .extracting(BidRequest::getSite) - .extracting(Site::getPublisher) - .allSatisfy(publisher -> { - final ExtPublisher publisherExt = (ExtPublisher) publisher.getExt(); - if (publisherExt != null && publisherExt.getProperties() != null - && publisherExt.getProperties().containsKey("params")) { - assertThat(publisherExt.getProperties().get("params")).isInstanceOf(ObjectNode.class); - assertThat(((ObjectNode) - publisherExt.getProperties().get("params")).has("networkId")).isFalse(); - } - }); + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&site_domain=site.sparteo.com"); } @Test - public void makeHttpRequestsShouldNotModifyPublisherExtWhenNetworkIdIsMissing() throws JsonProcessingException { + public void makeHttpRequestsShouldUseAppDomainWhenNoSite() { // given final ObjectNode impExt = mapper.createObjectNode(); - impExt.set("bidder", mapper.createObjectNode().put("someOtherParam", "someValue")); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); - final ObjectNode publisherExtJson = mapper.createObjectNode(); - publisherExtJson.put("existing", "value"); - final ExtPublisher originalPublisherExt = mapper.convertValue(publisherExtJson, ExtPublisher.class); + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(null) + .app(App.builder() + .domain("com.sparteo.app") + .publisher(Publisher.builder().id("p1").build()) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertMissingBundleWarning(result); + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&app_domain=com.sparteo.app&bundle=unknown"); + } + + @Test + public void makeHttpRequestsShouldSetNetworkIdOnAppPublisherExtWhenNoSite() { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); final BidRequest bidRequest = givenBidRequest( - request -> request.site(Site.builder().publisher( - Publisher.builder().ext(originalPublisherExt).build()).build()), - givenImp(imp -> imp.ext(impExt))); + r -> r + .site(null) + .app(App.builder() + .domain("com.sparteo.app") + .publisher(Publisher.builder().build()) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertMissingBundleWarning(result); + final BidRequest out = result.getValue().get(0).getPayload(); + assertThat(out.getApp()).isNotNull(); + assertThat(out.getApp().getPublisher()).isNotNull(); + + final ExtPublisher ext = out.getApp().getPublisher().getExt(); + assertThat(ext).isNotNull(); + assertThat(ext.getProperties()).containsKey("params"); + assertThat(ext.getProperties().get("params").get("networkId").asText()).isEqualTo("networkId"); + } + + @Test + public void makeHttpRequestsShouldCreateAppPublisherWhenMissingAndSetNetworkId() { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); + + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(null) + .app(App.builder() + .domain("com.sparteo.app") + .publisher(null) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertMissingBundleWarning(result); + final BidRequest out = result.getValue().get(0).getPayload(); + assertThat(out.getApp()).isNotNull(); + assertThat(out.getApp().getPublisher()).isNotNull(); + + final ExtPublisher ext = out.getApp().getPublisher().getExt(); + assertThat(ext).isNotNull(); + assertThat(ext.getProperties()).containsKey("params"); + assertThat(ext.getProperties().get("params").get("networkId").asText()).isEqualTo("networkId"); + } + + @Test + public void makeHttpRequestsShouldAppendBundleWhenAppBundlePresent() { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); + + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(null) + .app(App.builder() + .domain("dev.sparteo.com") + .bundle("com.sparteo.app") + .publisher(Publisher.builder().id("p1").build()) + .build()), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); @@ -396,25 +468,21 @@ public void makeHttpRequestsShouldNotModifyPublisherExtWhenNetworkIdIsMissing() // then assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .extracting(BidRequest::getSite) - .extracting(Site::getPublisher) - .extracting(Publisher::getExt) - .extracting(ext -> ((ExtPublisher) ext).getProperties()) - .allSatisfy(properties -> { - assertThat(properties.get("existing").asText()).isEqualTo("value"); - assertThat(properties).doesNotContainKey("params"); - }); + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&app_domain=dev.sparteo.com&bundle=com.sparteo.app"); } @Test - public void makeHttpRequestsShouldOverwriteInvalidSparteoExtWhenBidderExtIsValid() { + public void makeHttpRequestsShouldNotAppendBundleWhenNoAppBundle() { // given final ObjectNode impExt = mapper.createObjectNode(); - impExt.set("bidder", mapper.createObjectNode().put("param", "value")); - impExt.put("sparteo", "this_is_a_string"); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); - final BidRequest bidRequest = givenBidRequest(givenImp(imp -> imp.ext(impExt))); + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(Site.builder().domain("dev.sparteo.com").publisher(Publisher.builder().build()).build()) + .app(null), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); @@ -422,26 +490,109 @@ public void makeHttpRequestsShouldOverwriteInvalidSparteoExtWhenBidderExtIsValid // then assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()) - .extracting(HttpRequest::getPayload) - .flatExtracting(BidRequest::getImp) - .extracting(Imp::getExt) - .allSatisfy(ext -> { - assertThat(ext.at("/sparteo")).isInstanceOf(ObjectNode.class); - assertThat(ext.at("/sparteo/params/param").asText()).isEqualTo("value"); - }); + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&site_domain=dev.sparteo.com"); } @Test - public void makeHttpRequestsShouldReturnEmptyResultWhenRequestHasNoImps() { + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppPresentButBundleMissing() { // given - final BidRequest bidRequest = givenBidRequest(request -> request.imp(Collections.emptyList())); + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); + + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(null) + .app(App.builder() + .domain("dev.sparteo.com") + .publisher(Publisher.builder().id("p1").build()) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertMissingBundleWarning(result); + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&app_domain=dev.sparteo.com&bundle=unknown"); + } + + @Test + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppBundleIsNull() { + testAppendUnknownBundle(null); + } + + @Test + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppBundleIsEmpty() { + testAppendUnknownBundle(""); + } + + @Test + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppBundleIsBlank() { + testAppendUnknownBundle(" "); + } + + @Test + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppBundleIsLiteralNull() { + testAppendUnknownBundle("null"); + } + + @Test + public void makeHttpRequestsShouldAppendUnknownBundleWhenAppBundleIsLiteralNullIgnoreCase() { + testAppendUnknownBundle("NuLl"); + } + + private void testAppendUnknownBundle(String rawBundle) { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "networkId")); + + final BidRequest bidRequest = givenBidRequest( + r -> r + .site(null) + .app(App.builder() + .domain("dev.sparteo.com") + .bundle(rawBundle) + .publisher(Publisher.builder().id("p1").build()) + .build()), + givenImp(i -> i.ext(impExt))); + + // when + final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); + + // then + assertMissingBundleWarning(result); + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly("https://test.sparteo.com/endpoint?network_id=networkId&app_domain=dev.sparteo.com&bundle=unknown"); + } + + @Test + public void makeHttpRequestsShouldWarnAndSetUnknownAppDomainWhenAppDomainMissing() { + // given + final ObjectNode impExt = mapper.createObjectNode(); + impExt.set("bidder", mapper.createObjectNode().put("networkId", "nid")); + + final BidRequest bidRequest = givenBidRequest( + r -> r.site(null) + .app(App.builder() + .domain(null) + .bundle("com.example.bundle") + .publisher(Publisher.builder().build()) + .build()), + givenImp(i -> i.ext(impExt))); // when final Result>> result = sparteoBidder.makeHttpRequests(bidRequest); // then assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly( + "https://test.sparteo.com/endpoint?network_id=nid&app_domain=unknown&bundle=com.example.bundle"); } @Test @@ -522,12 +673,10 @@ public void makeBidsShouldReturnEmptyResultWhenBidResponseIsNull() throws JsonPr } @Test - public void makeBidsShouldReturnEmptyResultWhenBidResponseHasNoSeatBids() - throws JsonProcessingException { + public void makeBidsShouldReturnEmptyResultWhenBidResponseHasNoSeatBids() throws JsonProcessingException { // given final BidResponse bidResponse = BidResponse.builder().seatbid(Collections.emptyList()).build(); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -543,8 +692,7 @@ public void makeBidsShouldReturnBannerBidWhenMediaTypeIsBanner() throws JsonProc final Bid bid = givenBid(builder -> builder.impid("imp1").price(BigDecimal.valueOf(1.23)).adm("adm-banner"), BidType.banner.getName()); final BidResponse bidResponse = givenBidResponse(bid, "EUR"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -559,11 +707,10 @@ public void makeBidsShouldReturnBannerBidWhenMediaTypeIsBanner() throws JsonProc public void makeBidsShouldReturnVideoBidWhenMediaTypeIsVideo() throws JsonProcessingException { // given final Bid bid = givenBid(builder -> - builder.impid("imp2").price(BigDecimal.valueOf(2.34)).adm("adm-video"), + builder.impid("imp2").price(BigDecimal.valueOf(2.34)).adm("adm-video"), BidType.video.getName()); final BidResponse bidResponse = givenBidResponse(bid, "EUR"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -578,11 +725,10 @@ public void makeBidsShouldReturnVideoBidWhenMediaTypeIsVideo() throws JsonProces public void makeBidsShouldReturnNativeBidWhenMediaTypeIsNative() throws JsonProcessingException { // given final Bid bid = givenBid(builder -> - builder.impid("imp3").price(BigDecimal.valueOf(3.45)).adm("adm-native"), + builder.impid("imp3").price(BigDecimal.valueOf(3.45)).adm("adm-native"), BidType.xNative.getName()); final BidResponse bidResponse = givenBidResponse(bid, "EUR"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -603,8 +749,7 @@ public void makeBidsShouldReturnErrorForUnsupportedMediaTypeAndProcessOthers() t builder.impid("impBanner").price(BigDecimal.valueOf(2.0)), BidType.banner.getName()); final BidResponse bidResponse = givenBidResponse(List.of(audioBid, bannerBid), "EUR"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -624,8 +769,7 @@ public void makeBidsShouldReturnErrorWhenBidExtIsNull() throws JsonProcessingExc // given final Bid bid = givenBid(builder -> builder.impid("imp1").ext(null), null); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -642,8 +786,7 @@ public void makeBidsShouldReturnErrorWhenPrebidIsMissingInBidExt() throws JsonPr // given final Bid bid = givenBid(builder -> builder.impid("imp1").ext(mapper.createObjectNode()), null); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -660,8 +803,7 @@ public void makeBidsShouldReturnErrorWhenPrebidTypeIsMissingInBidExt() throws Js // given final Bid bid = givenBid(builder -> builder.impid("imp1").ext(createBidExtWithEmptyPrebid()), null); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -680,8 +822,7 @@ public void makeBidsShouldReturnErrorWhenPrebidCannotBeParsed() throws JsonProce malformedExt.putArray("prebid"); final Bid bid = givenBid(builder -> builder.impid("imp1").ext(malformedExt), null); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -698,8 +839,7 @@ public void makeBidsShouldReturnErrorWhenPrebidTypeIsUnsupported() throws JsonPr // given final Bid bid = givenBid(builder -> builder.impid("imp1"), "unknown-type"); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -715,15 +855,14 @@ public void makeBidsShouldReturnErrorWhenPrebidTypeIsUnsupported() throws JsonPr public void makeBidsShouldProcessValidBidsWhenSeatBidContainsNulls() throws JsonProcessingException { // given final Bid validBid = givenBid(builder -> - builder.impid("validImp").price(BigDecimal.ONE), + builder.impid("validImp").price(BigDecimal.ONE), BidType.banner.getName()); final List bids = new ArrayList<>(); bids.add(null); bids.add(validBid); final BidResponse bidResponse = givenBidResponse(bids, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -739,21 +878,20 @@ public void makeBidsShouldProcessValidBidsWhenSeatBidContainsNulls() throws Json public void makeBidsShouldCorrectlyProcessMultipleBidsAndSeatBids() throws JsonProcessingException { // given final Bid bid1 = givenBid(builder -> - builder.impid("imp1").price(BigDecimal.valueOf(1.0)), + builder.impid("imp1").price(BigDecimal.valueOf(1.0)), BidType.banner.getName()); final Bid bid2 = givenBid(builder -> - builder.impid("imp2").price(BigDecimal.valueOf(2.0)), + builder.impid("imp2").price(BigDecimal.valueOf(2.0)), BidType.video.getName()); final Bid bid3 = givenBid(builder -> - builder.impid("imp3").price(BigDecimal.valueOf(3.0)), + builder.impid("imp3").price(BigDecimal.valueOf(3.0)), BidType.xNative.getName()); final SeatBid seatBid1 = SeatBid.builder().bid(asList(bid1, bid2)).build(); final SeatBid seatBid2 = SeatBid.builder().bid(singletonList(bid3)).build(); final BidResponse bidResponse = BidResponse.builder().cur("USD").seatbid(asList(seatBid1, seatBid2)).build(); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -777,8 +915,7 @@ public void makeBidsShouldReturnErrorWhenPrebidExtIsNullNode() throws JsonProces final Bid bid = givenBid(builder -> builder.impid("imp1").ext(bidExtWithNullPrebid), null); final BidResponse bidResponse = givenBidResponse(bid, "USD"); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -794,10 +931,10 @@ public void makeBidsShouldReturnErrorWhenPrebidExtIsNullNode() throws JsonProces public void makeBidsShouldProcessValidSeatBidsWhenResponseContainsNulls() throws JsonProcessingException { // given final Bid validBid1 = givenBid(builder -> - builder.impid("validImp1").price(BigDecimal.TEN), + builder.impid("validImp1").price(BigDecimal.TEN), BidType.banner.getName()); final Bid validBid2 = givenBid(builder -> - builder.impid("validImp2").price(BigDecimal.ONE), + builder.impid("validImp2").price(BigDecimal.ONE), BidType.banner.getName()); final SeatBid validSeatBid1 = SeatBid.builder().bid(singletonList(validBid1)).build(); @@ -809,8 +946,7 @@ public void makeBidsShouldProcessValidSeatBidsWhenResponseContainsNulls() throws seatBidsWithNull.add(validSeatBid2); final BidResponse bidResponse = BidResponse.builder().cur("USD").seatbid(seatBidsWithNull).build(); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -829,8 +965,7 @@ public void makeBidsShouldReturnEmptyResultWhenSeatBidHasNullBidList() throws Js final SeatBid seatBidWithNullBids = SeatBid.builder().bid(null).build(); final BidResponse bidResponse = BidResponse.builder().cur("USD").seatbid(singletonList(seatBidWithNullBids)).build(); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -846,8 +981,7 @@ public void makeBidsShouldReturnEmptyResultWhenSeatBidHasEmptyBidList() throws J final SeatBid seatBidWithEmptyBids = SeatBid.builder().bid(Collections.emptyList()).build(); final BidResponse bidResponse = BidResponse.builder().cur("USD").seatbid(singletonList(seatBidWithEmptyBids)).build(); - final BidderCall httpCall = - givenHttpCall(givenBidRequest(), bidResponse); + final BidderCall httpCall = givenHttpCall(givenBidRequest(), bidResponse); // when final Result> result = sparteoBidder.makeBids(httpCall, givenBidRequest()); @@ -872,11 +1006,9 @@ private Imp givenImp(UnaryOperator impCustomizer) { private Bid givenBid(UnaryOperator bidCustomizer, String mediaType) { final Bid.BidBuilder builder = Bid.builder(); bidCustomizer.apply(builder); - if (builder.build().getExt() == null && mediaType != null) { builder.ext(createBidExtWithType(mediaType)); } - return builder.build(); } @@ -916,4 +1048,24 @@ private ObjectNode createBidExtWithEmptyPrebid() { bidExt.set("prebid", mapper.createObjectNode()); return bidExt; } + + private static void assertMissingSiteDomainWarning(Result result) { + assertThat(result.getErrors()) + .hasSize(1) + .allSatisfy(err -> { + assertThat(err.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(err.getMessage()) + .contains("Domain not found. Missing the site.domain or the site.page field"); + }); + } + + private static void assertMissingBundleWarning(Result result) { + assertThat(result.getErrors()) + .hasSize(1) + .allSatisfy(err -> { + assertThat(err.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(err.getMessage()) + .contains("Bundle not found. Missing the app.bundle field."); + }); + } } diff --git a/src/test/java/org/prebid/server/bidder/sparteo/SparteoUtilTest.java b/src/test/java/org/prebid/server/bidder/sparteo/SparteoUtilTest.java new file mode 100644 index 00000000000..d735c0d7e89 --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/sparteo/SparteoUtilTest.java @@ -0,0 +1,49 @@ +package org.prebid.server.bidder.sparteo; + +import org.junit.jupiter.api.Test; +import org.prebid.server.VertxTest; +import org.prebid.server.bidder.sparteo.util.SparteoUtil; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SparteoUtilTest extends VertxTest { + + @Test + public void normalizeHostnameShouldReturnBaseDomain() { + final String base = "dev.sparteo.com"; + + assertThat(SparteoUtil.normalizeHostname(base)).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname("DeV.SpArTeO.CoM")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname(" " + base + " ")).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname("www." + base)).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname("WWW." + base)).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname(base + ".")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname(base + "..")).isEqualTo(base + "."); + + assertThat(SparteoUtil.normalizeHostname("null")).isEqualTo(""); + assertThat(SparteoUtil.normalizeHostname("NuLl")).isEqualTo(""); + + assertThat(SparteoUtil.normalizeHostname("")).isEqualTo(""); + assertThat(SparteoUtil.normalizeHostname(" ")).isEqualTo(""); + + assertThat(SparteoUtil.normalizeHostname("www2." + base)).isEqualTo("www2." + base); + + assertThat(SparteoUtil.normalizeHostname("www." + base + ":8080")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname("DEV.SPARTEO.COM:443")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname(base + ".:8443")).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname(base + "/some/path?x=1")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname("www." + base + "/p")).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname(base + ":8080/p?q=1")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname("www." + base + ":3000/some/path")).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname("https://www." + base + "/x")).isEqualTo(base); + assertThat(SparteoUtil.normalizeHostname("http://WWW." + base + ":8080/abc")).isEqualTo(base); + + assertThat(SparteoUtil.normalizeHostname(" https://www." + base + ":3000/x ")).isEqualTo(base); + } +} diff --git a/src/test/java/org/prebid/server/it/SparteoTest.java b/src/test/java/org/prebid/server/it/SparteoTest.java index f169a486e50..713cf35fee1 100644 --- a/src/test/java/org/prebid/server/it/SparteoTest.java +++ b/src/test/java/org/prebid/server/it/SparteoTest.java @@ -5,6 +5,7 @@ import org.prebid.server.model.Endpoint; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; 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.urlPathEqualTo; @@ -15,6 +16,8 @@ public class SparteoTest extends IntegrationTest { @Test public void openrtb2AuctionShouldRespondWithBidsFromSparteoBanner() throws Exception { WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/sparteo-exchange")) + .withQueryParam("network_id", equalTo("networkId")) + .withQueryParam("site_domain", equalTo("dev.sparteo.com")) .withRequestBody(equalToJson( jsonFrom("openrtb2/sparteo/test-sparteo-bid-request.json"))) .willReturn(aResponse().withBody(