Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
153 changes: 151 additions & 2 deletions crates/trusted-server-adapter-fastly/src/route_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::sync::Arc;

use edgezero_core::key_value_store::NoopKvStore;
use error_stack::Report;
use fastly::http::StatusCode;
use fastly::http::{header, StatusCode};
use fastly::Request;
use trusted_server_core::auction::build_orchestrator;
use serde_json::json;
use trusted_server_core::auction::{build_orchestrator, AuctionOrchestrator};
use trusted_server_core::integrations::IntegrationRegistry;
use trusted_server_core::platform::{
ClientInfo, GeoInfo, PlatformBackend, PlatformBackendSpec, PlatformConfigStore, PlatformError,
Expand Down Expand Up @@ -162,6 +163,46 @@ fn create_test_settings() -> Settings {
settings
}

fn create_auction_test_settings_without_consent_store(providers: &str) -> Settings {
let config = format!(
r#"
[[handlers]]
path = "^/admin"
username = "admin"
password = "admin-pass"

[publisher]
domain = "test-publisher.com"
cookie_domain = ".test-publisher.com"
origin_url = "https://origin.test-publisher.com"
proxy_secret = "unit-test-proxy-secret"

[edge_cookie]
secret_key = "test-secret-key"

[request_signing]
enabled = false
config_store_id = "test-config-store-id"
secret_store_id = "test-secret-store-id"

[auction]
enabled = true
providers = {providers}
timeout_ms = 2000
"#,
);

Settings::from_toml(&config).expect("should parse adapter auction route test settings")
}
Comment thread
ChristianPavilonis marked this conversation as resolved.

fn build_route_stack(settings: &Settings) -> (AuctionOrchestrator, IntegrationRegistry) {
let orchestrator = build_orchestrator(settings).expect("should build auction orchestrator");
let integration_registry =
IntegrationRegistry::new(settings).expect("should create integration registry");

(orchestrator, integration_registry)
}

fn test_runtime_services(req: &Request) -> RuntimeServices {
RuntimeServices::builder()
.config_store(Arc::new(StubJwksConfigStore))
Expand All @@ -178,6 +219,45 @@ fn test_runtime_services(req: &Request) -> RuntimeServices {
.build()
}

fn route_auction(settings: &Settings, body: impl Into<Vec<u8>>) -> fastly::Response {
let (orchestrator, integration_registry) = build_route_stack(settings);
let req = Request::post("https://test.com/auction")
.with_header(header::CONTENT_TYPE, "application/json")
.with_body(body.into());
let services = test_runtime_services(&req);

futures::executor::block_on(route_request(
settings,
&orchestrator,
&integration_registry,
&services,
req,
))
.expect("should route auction request")
}

fn valid_banner_ad_unit_body() -> Vec<u8> {
serde_json::to_vec(&json!({
"adUnits": [
{
"code": "div-gpt-ad-1",
"mediaTypes": {
"banner": {
"sizes": [[300, 250]]
}
},
"bids": [
{
"bidder": "missing-provider",
"params": {}
}
]
}
]
}))
.expect("should serialize valid auction route test body")
}

#[test]
fn configured_missing_consent_store_only_breaks_consent_routes() {
let settings = create_test_settings();
Expand Down Expand Up @@ -249,3 +329,72 @@ fn configured_missing_consent_store_only_breaks_consent_routes() {
"should scope consent store failures to the consent-dependent routes"
);
}

#[test]
fn malformed_auction_json_returns_bad_request() {
let settings = create_auction_test_settings_without_consent_store(r#"["missing-provider"]"#);

let mut response = route_auction(&settings, "{not-json");

assert_eq!(
response.get_status(),
StatusCode::BAD_REQUEST,
"should reject malformed JSON as a client request error"
);
assert!(
response.take_body_str().contains("Bad request"),
"should return a client-facing bad request message"
);
}

#[test]
fn invalid_auction_banner_size_returns_bad_request() {
let settings = create_auction_test_settings_without_consent_store(r#"["missing-provider"]"#);
let body = serde_json::to_vec(&json!({
"adUnits": [
{
"code": "div-gpt-ad-1",
"mediaTypes": {
"banner": {
"sizes": [[300]]
}
}
}
]
}))
.expect("should serialize invalid auction route test body");

let response = route_auction(&settings, body);

assert_eq!(
response.get_status(),
StatusCode::BAD_REQUEST,
"should reject semantically invalid banner sizes as a client request error"
);
}

#[test]
fn valid_auction_request_with_no_providers_returns_bad_gateway() {
Comment thread
ChristianPavilonis marked this conversation as resolved.
Outdated
let settings = create_auction_test_settings_without_consent_store("[]");

let response = route_auction(&settings, valid_banner_ad_unit_body());

assert_eq!(
response.get_status(),
StatusCode::BAD_GATEWAY,
"should surface no-provider orchestration failures as gateway errors"
);
}

#[test]
fn valid_auction_request_with_unregistered_provider_returns_bad_gateway() {
Comment thread
ChristianPavilonis marked this conversation as resolved.
Outdated
let settings = create_auction_test_settings_without_consent_store(r#"["missing-provider"]"#);

let response = route_auction(&settings, valid_banner_ad_unit_body());

assert_eq!(
response.get_status(),
StatusCode::BAD_GATEWAY,
"should fail when configured providers cannot be launched"
);
}
Comment thread
ChristianPavilonis marked this conversation as resolved.
Outdated
2 changes: 1 addition & 1 deletion crates/trusted-server-core/src/auction/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub async fn handle_auction(
) -> Result<Response, Report<TrustedServerError>> {
// Parse request body
let body: AdRequest = serde_json::from_slice(&req.take_body_bytes()).change_context(
TrustedServerError::Auction {
TrustedServerError::BadRequest {
message: "Failed to parse auction request body".to_string(),
},
)?;
Expand Down
6 changes: 6 additions & 0 deletions crates/trusted-server-core/src/auction/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,12 @@ impl AuctionOrchestrator {
}
}

if pending_requests.is_empty() {
return Err(Report::new(TrustedServerError::Auction {
message: "No provider requests launched".to_string(),
Comment thread
ChristianPavilonis marked this conversation as resolved.
Outdated
}));
}

let deadline = Duration::from_millis(u64::from(context.timeout_ms));
log::info!(
"Launched {} concurrent requests, waiting for responses (timeout: {}ms)...",
Expand Down
Loading