From c7993f1aff3279692b213bba855d8f7a2dc042e0 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 2 Jun 2025 15:05:58 +0200 Subject: [PATCH 1/3] Cache endpoint split for response --- docs/config-app.md | 3 + .../prebid/server/cache/CoreCacheService.java | 28 +++-- .../spring/config/ServiceConfiguration.java | 57 +++++++-- .../server/cache/CoreCacheServiceTest.java | 117 ++++++++++++++++++ 4 files changed, 185 insertions(+), 20 deletions(-) diff --git a/docs/config-app.md b/docs/config-app.md index 5628341a812..ca0f53e51a0 100644 --- a/docs/config-app.md +++ b/docs/config-app.md @@ -284,6 +284,9 @@ For `JVM` metrics - `cache.scheme` - set the external Cache Service protocol: `http`, `https`, etc. - `cache.host` - set the external Cache Service destination in format `host:port`. - `cache.path` - set the external Cache Service path, for example `/cache`. +- `cache.internal.scheme` - set the internal Cache Service protocol: `http`, `https`, etc., the internal scheme get priority over the external one when provided. +- `cache.internal.host` - set the internal Cache Service destination in format `host:port`, the internal port get priority over the external one when provided. +- `cache.internal.path` - set the internal Cache Service path, for example `/cache`, the internal path get priority over the external one when provided. - `storage.pbc.enabled` - If set to true, this will allow storing modules’ data in third-party storage. - `storage.pbc.path` - set the external Cache Service path for module caching, for example `/pbc-storage`. - `cache.api-key-secured` - if set to `true`, will cause Prebid Server to add a special API key header to Prebid Cache requests. diff --git a/src/main/java/org/prebid/server/cache/CoreCacheService.java b/src/main/java/org/prebid/server/cache/CoreCacheService.java index d3a8b198b24..863c25ee38b 100644 --- a/src/main/java/org/prebid/server/cache/CoreCacheService.java +++ b/src/main/java/org/prebid/server/cache/CoreCacheService.java @@ -68,7 +68,8 @@ public class CoreCacheService { private static final int MAX_DATACENTER_REGION_LENGTH = 4; private final HttpClient httpClient; - private final URL endpointUrl; + private final URL externalEndpointUrl; + private final URL internalEndpointUrl; private final String cachedAssetUrlTemplate; private final long expectedCacheTimeMs; private final VastModifier vastModifier; @@ -86,7 +87,8 @@ public class CoreCacheService { public CoreCacheService( HttpClient httpClient, - URL endpointUrl, + URL externalEndpointUrl, + URL internalEndpointUrl, String cachedAssetUrlTemplate, long expectedCacheTimeMs, String apiKey, @@ -101,7 +103,8 @@ public CoreCacheService( JacksonMapper mapper) { this.httpClient = Objects.requireNonNull(httpClient); - this.endpointUrl = Objects.requireNonNull(endpointUrl); + this.externalEndpointUrl = Objects.requireNonNull(externalEndpointUrl); + this.internalEndpointUrl = internalEndpointUrl; this.cachedAssetUrlTemplate = Objects.requireNonNull(cachedAssetUrlTemplate); this.expectedCacheTimeMs = expectedCacheTimeMs; this.vastModifier = Objects.requireNonNull(vastModifier); @@ -121,13 +124,13 @@ public CoreCacheService( } public String getEndpointHost() { - final String host = endpointUrl.getHost(); - final int port = endpointUrl.getPort(); + final String host = externalEndpointUrl.getHost(); + final int port = externalEndpointUrl.getPort(); return port != -1 ? "%s:%d".formatted(host, port) : host; } public String getEndpointPath() { - return endpointUrl.getPath(); + return externalEndpointUrl.getPath(); } public String getCachedAssetURLTemplate() { @@ -142,7 +145,7 @@ public String cacheVideoDebugLog(CachedDebugLog cachedDebugLog, Integer videoCac makeDebugCacheCreative(cachedDebugLog, cacheKey, videoCacheTtl)); final BidCacheRequest bidCacheRequest = toBidCacheRequest(cachedCreatives); httpClient.post( - endpointUrl.toString(), + ObjectUtils.firstNonNull(internalEndpointUrl, externalEndpointUrl).toString(), cacheHeaders, mapper.encodeToString(bidCacheRequest), expectedCacheTimeMs); @@ -179,7 +182,7 @@ private Future makeRequest(BidCacheRequest bidCacheRequest, final long startTime = clock.millis(); return httpClient.post( - endpointUrl.toString(), + ObjectUtils.firstNonNull(internalEndpointUrl, externalEndpointUrl).toString(), cacheHeaders, mapper.encodeToString(bidCacheRequest), remainingTimeout) @@ -308,9 +311,9 @@ private Future doCacheOpenrtb(List bids, updateCreativeMetrics(accountId, cachedCreatives); - final String url = endpointUrl.toString(); + final String url = ObjectUtils.firstNonNull(internalEndpointUrl, externalEndpointUrl).toString(); final String body = mapper.encodeToString(bidCacheRequest); - final CacheHttpRequest httpRequest = CacheHttpRequest.of(url, body); + final CacheHttpRequest httpRequest = CacheHttpRequest.of(externalEndpointUrl.toString(), body); final long startTime = clock.millis(); return httpClient.post(url, cacheHeaders, body, remainingTimeout) @@ -336,7 +339,8 @@ private CacheServiceResult processResponseOpenrtb(HttpClientResponse response, final CacheHttpResponse httpResponse = CacheHttpResponse.of(response.getStatusCode(), response.getBody()); final int responseStatusCode = response.getStatusCode(); - final DebugHttpCall httpCall = makeDebugHttpCall(endpointUrl.toString(), httpRequest, httpResponse, startTime); + final DebugHttpCall httpCall = makeDebugHttpCall( + externalEndpointUrl.toString(), httpRequest, httpResponse, startTime); final BidCacheResponse bidCacheResponse; try { bidCacheResponse = toBidCacheResponse( @@ -359,7 +363,7 @@ private CacheServiceResult failResponseOpenrtb(Throwable exception, metrics.updateCacheRequestFailedTime(accountId, clock.millis() - startTime); - final DebugHttpCall httpCall = makeDebugHttpCall(endpointUrl.toString(), request, null, startTime); + final DebugHttpCall httpCall = makeDebugHttpCall(externalEndpointUrl.toString(), request, null, startTime); return CacheServiceResult.of(httpCall, exception, Collections.emptyMap()); } diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 1e496ea7e9a..4e9dd78e053 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -6,6 +6,7 @@ import io.vertx.core.file.FileSystem; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.net.JksOptions; +import lombok.Data; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.activity.ActivitiesConfigResolver; @@ -157,14 +158,9 @@ public class ServiceConfiguration { @Bean CoreCacheService cacheService( - @Value("${cache.scheme}") String scheme, - @Value("${cache.host}") String host, - @Value("${cache.path}") String path, - @Value("${cache.query}") String query, + CacheConfigurationProperties cacheConfigurationProperties, @Value("${auction.cache.expected-request-time-ms}") long expectedCacheTimeMs, @Value("${pbc.api.key:#{null}}") String apiKey, - @Value("${cache.api-key-secured:false}") boolean apiKeySecured, - @Value("${cache.append-trace-info-to-cache-id:false}") boolean appendTraceInfoToCacheId, @Value("${datacenter-region:#{null}}") String datacenterRegion, VastModifier vastModifier, EventsService eventsService, @@ -173,14 +169,25 @@ CoreCacheService cacheService( Clock clock, JacksonMapper mapper) { + final String scheme = cacheConfigurationProperties.getScheme(); + final String host = cacheConfigurationProperties.getHost(); + final String path = cacheConfigurationProperties.getPath(); + final String query = cacheConfigurationProperties.getQuery(); + final CacheConfigurationProperties.InternalCacheConfigurationProperties internalProperties = + cacheConfigurationProperties.getInternal(); + return new CoreCacheService( httpClient, CacheServiceUtil.getCacheEndpointUrl(scheme, host, path), + internalProperties == null ? null : CacheServiceUtil.getCacheEndpointUrl( + internalProperties.getScheme(), + internalProperties.getHost(), + internalProperties.getPath()), CacheServiceUtil.getCachedAssetUrlTemplate(scheme, host, path, query), expectedCacheTimeMs, apiKey, - apiKeySecured, - appendTraceInfoToCacheId, + cacheConfigurationProperties.isApiKeySecured(), + cacheConfigurationProperties.isAppendTraceInfoToCacheId(), datacenterRegion, vastModifier, eventsService, @@ -190,6 +197,40 @@ CoreCacheService cacheService( mapper); } + @Bean + @ConfigurationProperties(prefix = "cache") + CacheConfigurationProperties cacheConfigurationProperties() { + return new CacheConfigurationProperties(); + } + + @Data + private static class CacheConfigurationProperties { + + private String scheme; + + private String host; + + private String path; + + private String query; + + boolean apiKeySecured; + + boolean appendTraceInfoToCacheId; + + private InternalCacheConfigurationProperties internal; + + @Data + private static class InternalCacheConfigurationProperties { + + private String scheme; + + private String host; + + private String path; + } + } + @Bean @ConditionalOnProperty(prefix = "cache.module", name = "enabled", havingValue = "false", matchIfMissing = true) PbcStorageService noOpModuleCacheService() { diff --git a/src/test/java/org/prebid/server/cache/CoreCacheServiceTest.java b/src/test/java/org/prebid/server/cache/CoreCacheServiceTest.java index ebb573edbe0..6ce3836a695 100644 --- a/src/test/java/org/prebid/server/cache/CoreCacheServiceTest.java +++ b/src/test/java/org/prebid/server/cache/CoreCacheServiceTest.java @@ -108,6 +108,7 @@ public void setUp() throws MalformedURLException, JsonProcessingException { target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, null, @@ -233,6 +234,60 @@ public void cacheBidsOpenrtbShouldTolerateReadingHttpResponseFails() throws Json // then verify(metrics).updateCacheRequestFailedTime(eq("accountId"), anyLong()); + verify(httpClient).post(eq("http://cache-service/cache"), any(), any(), anyLong()); + + final CacheServiceResult result = future.result(); + assertThat(result.getCacheBids()).isEmpty(); + assertThat(result.getError()).isInstanceOf(RuntimeException.class).hasMessage("Response exception"); + + final CacheHttpRequest request = givenCacheHttpRequest(bidinfo.getBid()); + assertThat(result.getHttpCall()) + .isEqualTo(DebugHttpCall.builder() + .requestHeaders(givenDebugHeaders()) + .endpoint("http://cache-service/cache") + .requestBody(request.getBody()) + .requestUri(request.getUri()) + .responseTimeMillis(0) + .build()); + } + + @Test + public void cacheBidsOpenrtbShouldTryCallingInternalEndpointAndTolerateReadingHttpResponseFails() + throws JsonProcessingException, MalformedURLException { + + // given + target = new CoreCacheService( + httpClient, + new URL("http://cache-service/cache"), + new URL("http://cache-service-internal/cache"), + "http://cache-service-host/cache?uuid=", + 100L, + null, + false, + false, + null, + vastModifier, + eventsService, + metrics, + clock, + idGenerator, + jacksonMapper); + + givenHttpClientProducesException(new RuntimeException("Response exception")); + final BidInfo bidinfo = givenBidInfo(builder -> builder.id("bidId1")); + + // when + final Future future = target.cacheBidsOpenrtb( + singletonList(bidinfo), + givenAuctionContext(), + CacheContext.builder() + .shouldCacheBids(true) + .build(), + eventsContext); + + // then + verify(metrics).updateCacheRequestFailedTime(eq("accountId"), anyLong()); + verify(httpClient).post(eq("http://cache-service-internal/cache"), any(), any(), anyLong()); final CacheServiceResult result = future.result(); assertThat(result.getCacheBids()).isEmpty(); @@ -384,6 +439,7 @@ public void cacheBidsOpenrtbShouldUseApiKeyWhenProvided() throws MalformedURLExc target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -783,6 +839,8 @@ public void cachePutObjectsShould() throws IOException { timeout); // then + verify(httpClient).post(eq("http://cache-service/cache"), any(), any(), anyLong()); + verify(metrics).updateCacheCreativeSize(eq("account"), eq(12), eq(MetricName.json)); verify(metrics).updateCacheCreativeSize(eq("account"), eq(4), eq(MetricName.xml)); verify(metrics).updateCacheCreativeSize(eq("account"), eq(11), eq(MetricName.unknown)); @@ -816,12 +874,65 @@ public void cachePutObjectsShould() throws IOException { .containsExactly(modifiedFirstBidPutObject, modifiedSecondBidPutObject, modifiedThirdBidPutObject); } + @Test + public void cachePutObjectsShouldCallInternalCacheEndpointWhenProvided() throws IOException { + // given + target = new CoreCacheService( + httpClient, + new URL("http://cache-service/cache"), + new URL("http://cache-service-internal/cache"), + "http://cache-service-host/cache?uuid=", + 100L, + null, + false, + false, + null, + vastModifier, + eventsService, + metrics, + clock, + idGenerator, + jacksonMapper); + + final BidPutObject firstBidPutObject = BidPutObject.builder() + .type("json") + .bidid("bidId1") + .bidder("bidder1") + .timestamp(1L) + .value(new TextNode("vast")) + .ttlseconds(1) + .build(); + + given(vastModifier.modifyVastXml(any(), any(), any(), any(), anyString())) + .willReturn(new TextNode("modifiedVast")); + + // when + target.cachePutObjects(asList(firstBidPutObject), true, singleton("bidder1"), "account", "pbjs", timeout); + + // then + verify(httpClient).post(eq("http://cache-service-internal/cache"), any(), any(), anyLong()); + verify(metrics).updateCacheCreativeSize(eq("account"), eq(12), eq(MetricName.json)); + verify(metrics).updateCacheCreativeTtl(eq("account"), eq(1), eq(MetricName.json)); + + verify(vastModifier).modifyVastXml(true, singleton("bidder1"), firstBidPutObject, "account", "pbjs"); + + final BidPutObject modifiedFirstBidPutObject = firstBidPutObject.toBuilder() + .bidid(null) + .bidder(null) + .timestamp(null) + .value(new TextNode("modifiedVast")) + .build(); + + assertThat(captureBidCacheRequest().getPuts()).containsExactly(modifiedFirstBidPutObject); + } + @Test public void cachePutObjectsShouldUseApiKeyWhenProvided() throws MalformedURLException { // given target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -863,6 +974,7 @@ public void cacheBidsOpenrtbShouldPrependTraceInfoWhenEnabled() throws IOExcepti target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -932,6 +1044,7 @@ public void cacheBidsOpenrtbShouldPrependTraceInfoWithDatacenterWhenEnabled() th target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -1001,6 +1114,7 @@ public void cacheBidsOpenrtbShouldNotPrependTraceInfoToLowEntoryCacheIds() throw target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -1051,6 +1165,7 @@ public void cachePutObjectsShouldPrependTraceInfoWhenEnabled() throws IOExceptio target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -1098,6 +1213,7 @@ public void cachePutObjectsShouldPrependTraceInfoWithDatacenterWhenEnabled() thr target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", @@ -1145,6 +1261,7 @@ public void cachePutObjectsShouldNotPrependTraceInfoToPassedInKey() throws IOExc target = new CoreCacheService( httpClient, new URL("http://cache-service/cache"), + null, "http://cache-service-host/cache?uuid=", 100L, "ApiKey", From 90f63592f3bb3b45a214c4bde8c9c56fd41a1a5b Mon Sep 17 00:00:00 2001 From: Markiyan Mykush <95693607+marki1an@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:02:47 +0300 Subject: [PATCH 2/3] Test: Internal cache destination config (#3983) Co-authored-by: osulzhenko --- .../server/functional/tests/CacheSpec.groovy | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy index e145e5a972f..2352e333c16 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy @@ -14,14 +14,17 @@ import org.prebid.server.functional.model.response.auction.Adm import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils +import static org.prebid.server.functional.model.response.auction.ErrorType.CACHE import static org.prebid.server.functional.model.response.auction.MediaType.BANNER import static org.prebid.server.functional.model.response.auction.MediaType.VIDEO +import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer class CacheSpec extends BaseSpec { private static final String PBS_API_HEADER = 'x-pbc-api-key' private static final Integer MAX_DATACENTER_REGION_LENGTH = 4 private static final Integer DEFAULT_UUID_LENGTH = 36 + private static final Integer TARGETING_PARAM_NAME_MAX_LENGTH = 20 private static final String XML_CREATIVE_SIZE_ACCOUNT_METRIC = "account.%s.prebid_cache.creative_size.xml" private static final String JSON_CREATIVE_SIZE_ACCOUNT_METRIC = "account.%s.prebid_cache.creative_size.json" @@ -33,6 +36,12 @@ class CacheSpec extends BaseSpec { private static final String JSON_CREATIVE_SIZE_GLOBAL_METRIC = "prebid_cache.creative_size.json" private static final String CACHE_REQUEST_OK_GLOBAL_METRIC = "prebid_cache.requests.ok" + private static final String CACHE_PATH = "/${PBSUtils.randomString}".toString() + private static final String CACHE_HOST = "${PBSUtils.randomString}:${PBSUtils.getRandomNumber(0, 65535)}".toString() + private static final String INTERNAL_CACHE_PATH = '/cache' + private static final String HTTP_SCHEME = 'http' + private static final String HTTPS_SCHEME = 'https' + def "PBS should update prebid_cache.creative_size.xml metric when xml creative is received"() { given: "Current value of metric prebid_cache.requests.ok" def initialValue = getCurrentMetricValue(defaultPbsService, CACHE_REQUEST_OK_GLOBAL_METRIC) @@ -492,4 +501,145 @@ class CacheSpec extends BaseSpec { PBSUtils.getRandomCase(" inline ") | " ${PBSUtils.getRandomCase(" impression ")} $PBSUtils.randomNumber " " inline ${PBSUtils.getRandomString()} " | " ImpreSSion " } + + def "PBS shouldn't cache bids when targeting is specified and config cache is invalid"() { + given: "Pbs config with cache" + def INVALID_PREBID_CACHE_CONFIG = ["cache.path" : CACHE_PATH, + "cache.scheme": HTTP_SCHEME, + "cache.host" : CACHE_HOST] + def pbsService = pbsServiceFactory.getService(INVALID_PREBID_CACHE_CONFIG) + + and: "Default BidRequest with cache, targeting" + def bidRequest = BidRequest.defaultBidRequest.tap { + it.enableCache() + } + + when: "PBS processes auction request" + def bidResponse = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain error" + assert bidResponse.ext?.errors[CACHE]*.code == [999] + assert bidResponse.ext?.errors[CACHE]*.message[0] == ("Failed to resolve '${CACHE_HOST.tokenize(":")[0]}' [A(1)]") + + and: "Bid response targeting should contain value" + assert bidResponse.seatbid[0].bid[0].ext.prebid.targeting.findAll { it.key.startsWith("hb_cache") }.isEmpty() + + and: "PBS shouldn't call PBC" + assert prebidCache.getRequestCount(bidRequest.imp[0].id) == 0 + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(INVALID_PREBID_CACHE_CONFIG) + } + + def "PBS should cache bids and emit error when targeting is specified and config cache is valid and internal is invalid"() { + given: "Pbs config with cache" + def INVALID_PREBID_CACHE_CONFIG = ["cache.internal.path" : CACHE_PATH, + "cache.internal.scheme": HTTP_SCHEME, + "cache.internal.host" : CACHE_HOST] + def pbsService = pbsServiceFactory.getService(INVALID_PREBID_CACHE_CONFIG) + + and: "Default BidRequest with cache, targeting" + def bidRequest = BidRequest.defaultBidRequest.tap { + it.enableCache() + } + + when: "PBS processes auction request" + def bidResponse = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS should call PBC" + assert prebidCache.getRequestCount(bidRequest.imp[0].id) == 0 + + and: "Seat bid shouldn't be discarded" + assert !bidResponse.seatbid.isEmpty() + + and: "Bid response targeting should contain value" + assert bidResponse.seatbid[0].bid[0].ext.prebid.targeting.findAll { it.key.startsWith("hb_cache") }.isEmpty() + + and: "Debug should contain http call with empty response body" + def cacheCall = bidResponse.ext.debug.httpcalls['cache'][0] + assert cacheCall.responseBody == null + assert cacheCall.uri == "${HTTP_SCHEME}://${networkServiceContainer.hostAndPort + INTERNAL_CACHE_PATH}" + + then: "Response should contain error" + assert bidResponse.ext?.errors[CACHE]*.code == [999] + assert bidResponse.ext?.errors[CACHE]*.message[0] == ("Failed to resolve '${CACHE_HOST.tokenize(":")[0]}' [A(1)]") + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(INVALID_PREBID_CACHE_CONFIG) + } + + def "PBS should cache bids when targeting is specified and config cache is invalid and internal cache config valid"() { + given: "Pbs config with cache" + def INVALID_PREBID_CACHE_CONFIG = ["cache.path" : CACHE_PATH, + "cache.scheme": HTTPS_SCHEME, + "cache.host" : CACHE_HOST,] + def VALID_INTERNAL_CACHE_CONFIG = ["cache.internal.scheme": HTTP_SCHEME, + "cache.internal.host" : "$networkServiceContainer.hostAndPort".toString(), + "cache.internal.path" : INTERNAL_CACHE_PATH,] + def pbsService = pbsServiceFactory.getService(INVALID_PREBID_CACHE_CONFIG + VALID_INTERNAL_CACHE_CONFIG) + + and: "Default BidRequest with cache, targeting" + def bidRequest = BidRequest.defaultBidRequest.tap { + it.enableCache() + } + + when: "PBS processes auction request" + def bidResponse = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS shouldn't call PBC" + assert prebidCache.getRequestCount(bidRequest.imp[0].id) == 1 + + and: "Bid response targeting should contain value" + verifyAll (bidResponse?.seatbid[0]?.bid[0]?.ext?.prebid?.targeting as Map) { + it.get("hb_cache_id") + it.get("hb_cache_id_generic") + it.get("hb_cache_path") == CACHE_PATH + it.get("hb_cache_host") == CACHE_HOST + it.get("hb_cache_path_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == CACHE_PATH + it.get("hb_cache_host_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == CACHE_HOST + } + + and: "Debug should contain http call" + assert bidResponse.ext.debug.httpcalls['cache'][0].uri == + "${HTTPS_SCHEME}://${CACHE_HOST + CACHE_PATH}" + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(INVALID_PREBID_CACHE_CONFIG + VALID_INTERNAL_CACHE_CONFIG) + } + + def "PBS should cache bids when targeting is specified and config cache and internal cache config valid"() { + given: "Pbs config with cache" + def VALID_INTERNAL_CACHE_CONFIG = ["cache.internal.scheme": HTTP_SCHEME, + "cache.internal.host" : "$networkServiceContainer.hostAndPort".toString(), + "cache.internal.path" : INTERNAL_CACHE_PATH] + def pbsService = pbsServiceFactory.getService(VALID_INTERNAL_CACHE_CONFIG) + + and: "Default BidRequest with cache, targeting" + def bidRequest = BidRequest.defaultBidRequest.tap { + it.enableCache() + } + + when: "PBS processes auction request" + def bidResponse = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS should call PBC" + assert prebidCache.getRequestCount(bidRequest.imp[0].id) == 1 + + and: "Bid response targeting should contain value" + verifyAll (bidResponse.seatbid[0].bid[0].ext.prebid.targeting) { + it.get("hb_cache_id") + it.get("hb_cache_id_generic") + it.get("hb_cache_path") == INTERNAL_CACHE_PATH + it.get("hb_cache_host") == networkServiceContainer.hostAndPort.toString() + it.get("hb_cache_path_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == INTERNAL_CACHE_PATH + it.get("hb_cache_host_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == networkServiceContainer.hostAndPort.toString() + } + + and: "Debug should contain http call" + assert bidResponse.ext.debug.httpcalls['cache'][0].uri == + "${HTTP_SCHEME}://${networkServiceContainer.hostAndPort + INTERNAL_CACHE_PATH}" + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(VALID_INTERNAL_CACHE_CONFIG) + } } From 7033f8e8559e75d4745369bb7741f975099334da Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Tue, 10 Jun 2025 13:40:58 +0300 Subject: [PATCH 3/3] fix merge conflicts --- .../prebid/server/functional/tests/CacheSpec.groovy | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy index b3caa858735..b745ae53a1f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy @@ -126,6 +126,7 @@ class CacheSpec extends BaseSpec { def "PBS should cache bids without api-key header when targeting is specified and api-key-secured disabled"() { given: "Pbs config with disabled api-key-secured and pbc.api.key" def apiKey = PBSUtils.randomString + def pbsConfig = ['pbc.api.key': apiKey, 'cache.api-key-secured': 'false'] def pbsService = pbsServiceFactory.getService(['pbc.api.key': apiKey, 'cache.api-key-secured': 'false']) and: "Default BidRequest with cache, targeting" @@ -142,12 +143,16 @@ class CacheSpec extends BaseSpec { and: "PBS call shouldn't include api-key" assert !prebidCache.getRequestHeaders(bidRequest.imp[0].id)[PBS_API_HEADER] + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) } def "PBS should cache bids with api-key header when targeting is specified and api-key-secured enabled"() { given: "Pbs config with api-key-secured and pbc.api.key" def apiKey = PBSUtils.randomString - def pbsService = pbsServiceFactory.getService(['pbc.api.key': apiKey, 'cache.api-key-secured': 'true']) + def pbsConfig = ['pbc.api.key': apiKey, 'cache.api-key-secured': 'true'] + def pbsService = pbsServiceFactory.getService(pbsConfig) and: "Default BidRequest with cache, targeting" def bidRequest = BidRequest.defaultBidRequest.tap { @@ -163,6 +168,9 @@ class CacheSpec extends BaseSpec { and: "PBS call should include api-key" assert prebidCache.getRequestHeaders(bidRequest.imp[0].id)[PBS_API_HEADER] == [apiKey] + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) } def "PBS should cache banner bids with cache key that include account and datacenter short name when append-trace-info-to-cache-id enabled"() {