diff --git a/.github/workflows/ads-client-tests.yaml b/.github/workflows/ads-client-tests.yaml new file mode 100644 index 0000000000..aaf9af29b8 --- /dev/null +++ b/.github/workflows/ads-client-tests.yaml @@ -0,0 +1,28 @@ +name: Ads Client Tests + +on: + push: + branches: [ main ] + paths: + - 'components/ads-client/**' + pull_request: + branches: [ main ] + paths: + - 'components/ads-client/**' + + workflow_dispatch: + +jobs: + integration-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source $HOME/.cargo/env + rustup toolchain install + - name: Run ads-client integration tests against MARS staging + run: cargo test -p ads-client --test integration_test -- --ignored diff --git a/Cargo.lock b/Cargo.lock index 4e0d6b2cd6..dcb2571c87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "chrono", "context_id", "error-support", + "jsonschema", "mockall", "mockito", "once_cell", @@ -38,6 +39,7 @@ dependencies = [ "uuid", "viaduct", "viaduct-dev", + "viaduct-hyper", ] [[package]] @@ -51,6 +53,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.5" @@ -60,6 +76,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -183,6 +205,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -213,6 +241,28 @@ dependencies = [ "url", ] +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "axum" version = "0.6.19" @@ -224,9 +274,9 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "itoa", "matchit", "memchr", @@ -238,9 +288,9 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "tokio", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -254,8 +304,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "mime", "rustversion", "tower-layer", @@ -328,7 +378,7 @@ version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -340,6 +390,21 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -348,11 +413,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -370,12 +435,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "byteorder" version = "1.4.3" @@ -474,15 +551,22 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -510,7 +594,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -635,6 +719,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -645,6 +738,16 @@ dependencies = [ "unicode-width 0.1.11", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console" version = "0.15.5" @@ -707,11 +810,21 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core_maths" @@ -902,6 +1015,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "deflate64" version = "0.1.9" @@ -1040,6 +1159,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ece" version = "2.3.1" @@ -1060,6 +1185,15 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + [[package]] name = "embedded-uniffi-bindgen" version = "0.1.0" @@ -1472,6 +1606,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "1.7.0" @@ -1504,6 +1649,12 @@ dependencies = [ "uniffi", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "find-places-db" version = "0.1.0" @@ -1531,6 +1682,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-uri" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1543,6 +1705,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1567,6 +1735,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "fragile" version = "2.0.0" @@ -1579,6 +1757,12 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd79fa345a495d3ae89fb7165fec01c0e72f41821d642dda363a1e97975652e" +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -1731,27 +1915,27 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.13.3+wasi-0.2.2", + "r-efi", + "wasip2", "wasm-bindgen", - "windows-targets 0.52.6", ] [[package]] @@ -1788,7 +1972,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.9", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", "indexmap 2.5.0", "slab", "tokio", @@ -1820,7 +2023,18 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "foldhash", + "foldhash 0.1.4", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -1893,6 +2107,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -1900,7 +2124,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.9", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1912,9 +2159,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1938,20 +2185,58 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.9", + "http-body 0.4.5", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.4.0", + "hyper 1.8.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1959,12 +2244,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.27", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -2255,9 +2563,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] [[package]] name = "is-docker" @@ -2337,6 +2655,28 @@ dependencies = [ "regex", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.32" @@ -2356,6 +2696,35 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonschema" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dcfbe6df48e0121219eefc8d6a30b872ac2769c7896454bda06f7b64129fa22" +dependencies = [ + "ahash", + "bytecount", + "data-encoding", + "email_address", + "fancy-regex", + "fraction", + "getrandom 0.3.4", + "idna", + "itoa", + "num-cmp", + "num-traits", + "percent-encoding", + "referencing", + "regex", + "regex-syntax", + "reqwest 0.13.2", + "rustls", + "serde", + "serde_json", + "unicode-general-category", + "uuid-simd", +] + [[package]] name = "jwcrypto" version = "0.1.0" @@ -2393,9 +2762,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" @@ -2419,7 +2788,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", "libc", "redox_syscall 0.5.17", ] @@ -2482,11 +2851,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -2739,7 +3107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -2797,10 +3165,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.5", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.6.1", "security-framework-sys", "tempfile", ] @@ -2842,7 +3210,7 @@ dependencies = [ "copypasta", "glob", "heck", - "hyper", + "hyper 0.14.27", "local-ip-address", "nimbus-fml", "percent-encoding", @@ -2853,8 +3221,8 @@ dependencies = [ "termcolor", "thiserror 2.0.3", "tokio", - "tower", - "tower-http", + "tower 0.4.13", + "tower-http 0.4.2", "tower-livereload", "unicode-segmentation", "update-informer", @@ -2975,16 +3343,86 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.2.0" +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -3101,7 +3539,7 @@ version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -3127,6 +3565,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-src" version = "300.3.1+3.3.1" @@ -3164,11 +3608,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3176,15 +3626,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.13", + "redox_syscall 0.5.17", "smallvec", - "windows-sys 0.36.1", + "windows-link 0.2.1", ] [[package]] @@ -3478,6 +3928,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -3505,7 +3961,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.17", ] [[package]] @@ -3592,7 +4048,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", ] [[package]] @@ -3607,11 +4063,46 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.17", "redox_syscall 0.2.13", "thiserror 1.0.69", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "referencing" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37add1aa1d619a975521d262d09f100f1f767791c9386c03679450f30acd78c1" +dependencies = [ + "ahash", + "fluent-uri", + "getrandom 0.3.4", + "hashbrown 0.16.1", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" version = "1.11.1" @@ -3723,10 +4214,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.26", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "hyper-tls", "ipnet", "js-sys", @@ -3749,6 +4240,45 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", + "tower 0.5.3", + "tower-http 0.6.8", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "restmail-client" version = "0.1.0" @@ -3760,6 +4290,20 @@ dependencies = [ "viaduct", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkv" version = "0.20.0" @@ -3768,7 +4312,7 @@ checksum = "0f67a9dbc634fcd36a2d1d800ca818065dcf71a1d907dc35130c2d1552c6e1dc" dependencies = [ "arrayref", "bincode", - "bitflags 2.8.0", + "bitflags 2.11.0", "id-arena", "lazy_static", "log", @@ -3786,7 +4330,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3846,6 +4390,80 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework 3.7.0", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -3939,7 +4557,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.3", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3947,9 +4578,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -4185,6 +4816,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "sql-support" version = "0.1.0" @@ -4400,6 +5041,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -4592,7 +5242,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio-macros", "windows-sys 0.48.0", ] @@ -4618,6 +5268,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.3" @@ -4713,18 +5373,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ac8060a61f8758a61562f6fb53ba3cbe1ca906f001df2e53cccddcdbee91e7c" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "http-range-header", "httpdate", "mime", @@ -4738,11 +5413,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-livereload" @@ -4751,18 +5444,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e90e6da0427c5e111e03c764d49c4e970f5a9f6569fe408e5a1cbe257f48388" dependencies = [ "bytes", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "pin-project-lite", "tokio", - "tower", + "tower 0.4.13", ] [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -4866,6 +5559,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-general-category" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -5032,6 +5731,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "update-informer" version = "1.0.0" @@ -5068,10 +5773,20 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.4", "serde", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -5122,7 +5837,7 @@ version = "0.2.0" dependencies = [ "async-trait", "error-support", - "hyper", + "hyper 0.14.27", "hyper-tls", "tokio", "uniffi", @@ -5136,10 +5851,16 @@ version = "0.2.0" dependencies = [ "error-support", "once_cell", - "reqwest", + "reqwest 0.11.18", "viaduct", ] +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "walkdir" version = "2.5.0" @@ -5167,12 +5888,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -5378,6 +6099,15 @@ dependencies = [ "webext-storage", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weedle2" version = "5.0.0" @@ -5443,6 +6173,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.36.1" @@ -5462,13 +6198,22 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.0", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm 0.42.0", - "windows_x86_64_msvc 0.42.0", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -5480,6 +6225,39 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.0" @@ -5513,9 +6291,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" @@ -5537,9 +6315,9 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -5561,9 +6339,9 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -5591,9 +6369,9 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -5615,9 +6393,9 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -5633,9 +6411,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -5657,9 +6435,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -5701,13 +6479,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.33.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" -dependencies = [ - "bitflags 2.8.0", -] +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" @@ -5803,6 +6578,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "zerofrom" version = "0.1.5" @@ -5892,7 +6687,7 @@ dependencies = [ "deflate64", "displaydoc", "flate2", - "getrandom 0.3.1", + "getrandom 0.3.4", "hmac", "indexmap 2.5.0", "lzma-rs", diff --git a/components/ads-client/Cargo.toml b/components/ads-client/Cargo.toml index 67d93c1ed5..5457e95a46 100644 --- a/components/ads-client/Cargo.toml +++ b/components/ads-client/Cargo.toml @@ -31,9 +31,11 @@ viaduct = { path = "../viaduct" } sql-support = { path = "../support/sql" } [dev-dependencies] +jsonschema = "0.43" mockall = "0.12" mockito = { version = "0.31", default-features = false } viaduct-dev = { path = "../support/viaduct-dev" } +viaduct-hyper = { path = "../support/viaduct-hyper" } [build-dependencies] uniffi = { version = "0.31", features = ["build"] } diff --git a/components/ads-client/openapi.json b/components/ads-client/openapi.json new file mode 100644 index 0000000000..388f249223 --- /dev/null +++ b/components/ads-client/openapi.json @@ -0,0 +1,1397 @@ +{ + "openapi": "3.0.0", + "security": [], + "servers": [ + { + "url": "https://ads.mozilla.org" + } + ], + "info": { + "description": "Mozilla Ad Routing Service", + "version": "1.0", + "title": "MARS" + }, + "paths": { + "/v1/ads": { + "post": { + "summary": "Get Unified API ads", + "operationId": "getAds", + "parameters": [ + { + "in": "header", + "name": "X-User-Agent", + "description": "For OHTTP requests, the X-User-Agent header must be populated with a valid User Agent string. This string will typically be acquired from a /v1/ads-preflight response.\n\nFor non-OHTTP requests, the header should be omitted.", + "schema": { + "type": "string" + } + }, + { + "in": "header", + "name": "X-Geo-Location", + "description": "For OHTTP requests, the X-Geo-Location header must be populated with a valide geo location string. This string will typically be acquired from a /v1/ads-preflight response.\n\nFor non-OHTTP requests, the header should be omitted.", + "schema": { + "type": "string", + "pattern": "^(\\d+)?,(\\w+)?,(\\w+)?$", + "example": "506,MA,US" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response. The response can still be empty, no error codes will be returned if no ads can be served after being filtered for correctness and being on the block list.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdResponse" + } + } + } + }, + "400": { + "description": "Bad Request. Requests are invalid if they contain unsupported placements or request too many ads for a placement." + } + } + } + }, + "/v1/ads-preflight": { + "get": { + "summary": "Preflight request for OHTTP callers", + "description": "This endpoint is used to obtain information that OHTTP callers will need to include in OHTTP requests to the /v1/ads endpoint.", + "parameters": [], + "responses": { + "200": { + "description": "Successful response. The response can still be empty, no error codes will be returned if no ads are available for the given device and location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreflightResponse" + } + } + } + }, + "400": { + "description": "Bad Request. Requests are expected to include a User-Agent header." + } + } + } + }, + "/v1/t": { + "get": { + "summary": "Report ad interaction", + "description": "Interaction callback URLs are returned in an ad response. When the corresponding action on the client occurs, those URLs should be fetched.", + "parameters": [ + { + "name": "data", + "in": "query", + "description": "Encoded interaction data", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "placement_id", + "in": "query", + "description": "Identifier representing the instance of the placement (different identifier than placement from the ad request), used only in special situations.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "position", + "in": "query", + "description": "Identifier indicating the position of the placement (optional). May be a string or numeric. If a numeric index is used it must be 0-based.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "reason", + "in": "query", + "description": "Identifier indicating the reason for the ad reporting interaction. Used only for, and required with, the 'report' action.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "inappropriate", + "not_interested", + "seen_too_many_times" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response" + } + } + } + }, + "/v1/log": { + "get": { + "summary": "Record client events", + "description": "This endpoint can be used to persist a prometheus metric.", + "parameters": [ + { + "name": "event", + "in": "query", + "description": "Identifier of the event to capture.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "init", + "error" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response" + }, + "400": { + "description": "Bad Request. Requests are invalid if they contain unsupported or empty events." + } + } + } + }, + "/v1/delete_user": { + "delete": { + "summary": "Delete user data", + "description": "Delete any data persisted associated with a given context_id.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "context_id" + ], + "properties": { + "context_id": { + "type": "string", + "format": "uuid", + "example": "12347fff-00b0-aaaa-0978-189231239808" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Successfully deleted user data." + } + } + } + }, + "/v1/images": { + "get": { + "summary": "Get ad image", + "description": "Proxies an ad image from an encoded URL. Encoded image URLs are returned in an ad response, calls to this endpoint shouldn't be constructed manually.", + "parameters": [ + { + "name": "image_data", + "in": "query", + "description": "Encoded ad image url", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response" + } + } + } + }, + "/v1/attribution": { + "get": { + "summary": "Get attribution reporting configuration", + "description": "The attribution reporting configuration is returned for the partner ID specified in the URL.", + "parameters": [ + { + "name": "partner_id", + "in": "query", + "description": "The ID of the partner.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "295beef7-1e3b-4128-b8f8-858e12aa660b" + } + ], + "responses": { + "200": { + "description": "Successful response. Attribution reporting configuration found for the specified partner ID.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Task" + } + } + } + }, + "204": { + "description": "Not Found. Returned when no attribution reporting configuration is found for the specified partner ID." + }, + "400": { + "description": "Bad Request. Requests are invalid if the parameter includes an invalid partner ID query parameter." + } + } + } + }, + "/spocs": { + "post": { + "operationId": "getSpocs", + "summary": "(legacy) Get sponsored content", + "description": "Get a list of spocs based on region and pocket_id. The IP address is used to deduce a rough geographic region, for example \"Texas\" in the U.S. or \"England\" in the U.K. The IP is not stored or shared to preserve privacy.", + "parameters": [ + { + "in": "query", + "name": "site", + "schema": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 2147483647 + }, + "required": false, + "description": "override siteId in ad decision requests", + "example": 2500 + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + }, + "required": false, + "description": "override region in keywords of ad decision requests for testing" + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + }, + "required": false, + "description": "override country in keywords of ad decision requests for testing" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpocRequest" + }, + "examples": { + "version_one": { + "summary": "Request from client that does not support collections, FireFox version <= 74", + "value": { + "version": 1, + "consumer_key": "40249-e88c401e1b1f2242d9e441c4", + "pocket_id": "{12345678-8901-2345-aaaa-bbbbbbcccccc}" + } + }, + "version_one_collection_req": { + "summary": "Request for collection placements with version=1", + "value": { + "version": 1, + "consumer_key": "40249-e88c401e1b1f2242d9e441c4", + "pocket_id": "{12345678-8901-2345-aaaa-bbbbbbcccccc}", + "placements": [ + { + "name": "collections-div", + "ad_types": [ + 1234 + ], + "zone_ids": [ + 5000 + ], + "count": 10 + } + ] + } + }, + "version_two_collection_req": { + "summary": "Request for collection placements with version=2", + "value": { + "version": 2, + "consumer_key": "40249-e88c401e1b1f2242d9e441c4", + "pocket_id": "{12345678-8901-2345-aaaa-bbbbbbcccccc}", + "placements": [ + { + "name": "collections-div", + "ad_types": [ + 1234 + ], + "zone_ids": [ + 5000 + ], + "count": 10 + } + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Responds with settings and a list of spocs.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/SpocFeed" + }, + "properties": { + "settings": { + "$ref": "#/components/schemas/Settings" + }, + "__debug__": { + "description": "Informational object returned in non-prod environments", + "type": "object", + "additionalProperties": true + } + } + } + } + } + } + } + } + }, + "/user": { + "delete": { + "operationId": "deleteUser", + "summary": "(legacy) Delete a user's personal data", + "description": "Used when a user opts-out of sponsored content to delete the user's data.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "pocket_id" + ], + "properties": { + "pocket_id": { + "description": "ID that uniquely identifies a session.", + "example": "{12345678-8901-2345-aaaa-bbbbbbcccccc}", + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Successfully deleted user data." + } + } + } + }, + "/v1/tiles": { + "get": { + "operationId": "getTiles", + "summary": "(legacy) Get tiles", + "responses": { + "200": { + "description": "Get a list of tiles based on region. The IP address is used to deduce a rough geographic region, for example \"Texas\" in the U.S. or \"England\" in the U.K.", + "headers": { + "cache-control": { + "schema": { + "type": "string" + }, + "description": "indicates tiles valid duration." + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tiles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LegacyTile" + } + }, + "sov": { + "type": "string", + "description": "SoV configuration", + "example": "kdfsi90wfglmnds" + } + } + } + } + } + }, + "204": { + "description": "No tiles available" + }, + "403": { + "description": "Tiles service is for Firefox only" + } + } + } + } + }, + "components": { + "schemas": { + "SpocRequest": { + "type": "object", + "required": [ + "version", + "consumer_key", + "pocket_id" + ], + "additionalProperties": false, + "properties": { + "version": { + "type": "integer", + "description": "API version", + "format": "int32", + "minimum": 1, + "maximum": 2, + "example": 2 + }, + "consumer_key": { + "type": "string", + "description": "Identifies that the request is coming from Firefox.", + "example": "40249-e88c401e1b1f2242d9e441c4" + }, + "pocket_id": { + "type": "string", + "description": "ID that uniquely identifies a session.", + "example": "{12345678-8901-2345-aaaa-bbbbbbcccccc}", + "pattern": "^\\{[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\\}$" + }, + "placements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Placement" + } + }, + "site": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 2147483647, + "description": "override siteId in ad decision requests", + "example": 2500 + }, + "country": { + "type": "string", + "description": "override country in keywords of ad decision requests for testing" + }, + "region": { + "type": "string", + "description": "override region in keywords of ad decision requests for testing" + } + } + }, + "Placement": { + "type": "object", + "description": "Placement describes parameters for a set of ads to return", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "example": "spocs", + "description": "Corresponds to the key in the response object." + }, + "ad_types": { + "type": "array", + "description": "IDs of Ad Types, indicating the size & dimensions of the ads to return.", + "items": { + "type": "integer", + "format": "int32", + "example": 1234, + "minimum": 1, + "maximum": 2147483647 + } + }, + "zone_ids": { + "type": "array", + "description": "ID of Zones, indicating what area these ads will be shown.", + "items": { + "type": "integer", + "format": "int32", + "example": 123456, + "minimum": 1, + "maximum": 2147483647 + } + }, + "count": { + "type": "integer", + "example": 20, + "minimum": 1, + "maximum": 20, + "description": "number of ads to return for this placement" + } + } + }, + "Settings": { + "type": "object", + "additionalProperties": false, + "required": [ + "feature_flags", + "spocsPerNewTabs", + "domainAffinityParameterSets", + "timeSegments" + ], + "properties": { + "spocsPerNewTabs": { + "type": "integer", + "minimum": 1, + "example": 1 + }, + "domainAffinityParameterSets": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/DomainAffinityParameterSet" + } + }, + "timeSegments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeSegment" + } + }, + "feature_flags": { + "type": "object", + "$ref": "#/components/schemas/FeatureFlags" + } + } + }, + "FeatureFlags": { + "type": "object", + "additionalProperties": false, + "required": [ + "spoc_v2", + "collections" + ], + "properties": { + "spoc_v2": { + "type": "boolean" + }, + "collections": { + "type": "boolean" + } + } + }, + "DomainAffinityParameterSet": { + "type": "object", + "additionalProperties": false, + "required": [ + "recencyFactor", + "frequencyFactor", + "combinedDomainFactor", + "perfectCombinedDomainScore", + "multiDomainBoost", + "itemScoreFactor" + ], + "properties": { + "recencyFactor": { + "type": "number" + }, + "frequencyFactor": { + "type": "number" + }, + "combinedDomainFactor": { + "type": "number" + }, + "perfectFrequencyVisits": { + "type": "number" + }, + "perfectCombinedDomainScore": { + "type": "number" + }, + "multiDomainBoost": { + "type": "number" + }, + "itemScoreFactor": { + "type": "number" + } + } + }, + "TimeSegment": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "startTime", + "endTime", + "weightPosition" + ], + "properties": { + "id": { + "type": "string" + }, + "startTime": { + "type": "integer" + }, + "endTime": { + "type": "integer" + }, + "weightPosition": { + "example": 1 + } + } + }, + "SpocFeed": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpocFeedItem" + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "title", + "flight_id" + ], + "properties": { + "title": { + "type": "string", + "example": "Best of the Web" + }, + "flight_id": { + "type": "integer", + "example": 4321 + }, + "sponsor": { + "type": "string", + "example": "AdvertiserName" + }, + "context": { + "type": "string", + "example": "Sponsored by AdvertiserName" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpocFeedItem" + } + } + } + } + ] + }, + "Shim": { + "type": "object", + "additionalProperties": false, + "properties": { + "click": { + "type": "string", + "example": "1234123asdf4tYadsfQ,xY-01BU12" + }, + "impression": { + "type": "string", + "example": "a0c3943asdf4tYadsf300,xY-01BU9aadc" + }, + "delete": { + "type": "string", + "example": "fdea123asdf4tYadsf1000,xY-01BUa654" + }, + "save": { + "type": "string", + "example": "4567123asdf4tYadsfQcda,xY-01BU123" + } + } + }, + "Caps": { + "type": "object", + "additionalProperties": false, + "required": [ + "lifetime", + "flight", + "campaign" + ], + "properties": { + "lifetime": { + "type": "integer", + "example": 50 + }, + "flight": { + "type": "object", + "additionalProperties": false, + "required": [ + "count", + "period" + ], + "properties": { + "count": { + "type": "integer", + "example": 10 + }, + "period": { + "type": "integer", + "description": "Period in seconds", + "example": 86400 + } + } + }, + "campaign": { + "type": "object", + "additionalProperties": false, + "required": [ + "count", + "period" + ], + "properties": { + "count": { + "type": "integer", + "example": 10 + }, + "period": { + "type": "integer", + "description": "Period in seconds", + "example": 86400 + } + } + } + } + }, + "SpocFeedItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "campaign_id": { + "type": "integer", + "example": 784 + }, + "caps": { + "type": "object", + "$ref": "#/components/schemas/Caps" + }, + "collection_title": { + "type": "string", + "description": "Shared title if all ads are one collection" + }, + "context": { + "type": "string", + "description": "Deprecated. Use sponsor field instead.", + "example": "Sponsored by AdvertiserName" + }, + "cta": { + "type": "string", + "description": "Text to display on CTA button", + "example": "Learn more" + }, + "domain": { + "type": "string", + "example": "mozilla.net" + }, + "domain_affinities": { + "type": "object", + "additionalProperties": { + "type": "number" + }, + "example": { + "vanguard.com": 0.9956, + "wealthsimple.com": 0.9193 + } + }, + "excerpt": { + "type": "string", + "example": "Driving excerpt" + }, + "flight_id": { + "type": "integer", + "example": 432 + }, + "id": { + "type": "integer", + "example": 30295 + }, + "image_src": { + "type": "string", + "example": "https://img-getpocket.cdn.mozilla.net/ad.gif" + }, + "is_video": { + "type": "boolean" + }, + "item_score": { + "type": "number", + "format": "float", + "example": 0.2 + }, + "min_score": { + "type": "number", + "format": "float", + "example": 0.1 + }, + "parameter_set": { + "type": "string", + "example": "default" + }, + "personalization_models": { + "type": "object", + "additionalProperties": true + }, + "priority": { + "type": "integer", + "description": "The priority order. 1-100, 1 is highest priority.", + "minimum": 1, + "maximum": 100 + }, + "raw_image_src": { + "type": "string", + "example": "https://mozilla.net/ad.gif" + }, + "shim": { + "type": "object", + "$ref": "#/components/schemas/Shim" + }, + "sponsor": { + "type": "string", + "example": "AdvertiserName" + }, + "sponsored_by_override": { + "type": "string", + "example": "AdvertiserName" + }, + "title": { + "type": "string", + "example": "Why driving is hard—even for AIs" + }, + "url": { + "type": "string", + "example": "http://mozilla.net/page" + } + } + }, + "LegacyTile": { + "type": "object", + "description": "tile format", + "required": [ + "id", + "name", + "url", + "click_url", + "image_url", + "image_size", + "impression_url" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "integer", + "format": "int32", + "example": 1234, + "description": "Partner specific id for ad", + "minimum": 1, + "maximum": 2147483647 + }, + "name": { + "type": "string", + "example": "Example COM", + "description": "Advertiser name" + }, + "url": { + "type": "string", + "example": "https://www.example.com/desktop_macos", + "description": "Advertiser URL" + }, + "click_url": { + "type": "string", + "example": "https://example.com/desktop_macos?version=16.0.0&key=22.1&ci=6.2&ctag=1612376952400200000", + "description": "Click counting URL" + }, + "image_url": { + "type": "string", + "example": "https://example.com/desktop_macos01.jpg", + "description": "Ad image" + }, + "image_size": { + "type": "integer", + "nullable": true, + "format": "int32", + "example": 200, + "description": "Image size" + }, + "impression_url": { + "type": "string", + "example": "https://example.com/desktop_macos?id=0001", + "description": "Impression counting URL" + } + } + }, + "AdPlacement": { + "type": "object", + "properties": { + "placement": { + "type": "string", + "example": "placement_1", + "description": "Specifies the placement location of the ad. Values will be Mozilla supplied and specific to the integration." + }, + "count": { + "type": "integer", + "format": "int32", + "default": 1, + "minimum": 1, + "maximum": 20, + "description": "The number of ads to be placed in the specified location." + }, + "content": { + "type": "object", + "$ref": "#/components/schemas/AdContent" + } + }, + "required": [ + "placement" + ] + }, + "AdRequest": { + "type": "object", + "properties": { + "context_id": { + "type": "string", + "format": "uuid", + "example": "12347fff-00b0-aaaa-0978-189231239808", + "description": "An identifier for the user's context." + }, + "placements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AdPlacement" + }, + "minItems": 1, + "description": "A list of `AdPlacement` objects, specifying where ads should be placed." + }, + "blocks": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "CAISEm15IHNwZWNpYWwgc3BvbnNvcg" + ], + "description": "A list of strings specifying blocked content. The string values come from the `block_key` field in returned ads." + }, + "consent": { + "type": "object", + "$ref": "#/components/schemas/Consent" + } + }, + "required": [ + "context_id", + "placements" + ] + }, + "AdContent": { + "type": "object", + "properties": { + "taxonomy": { + "type": "string", + "enum": [ + "IAB-1.0", + "IAB-2.0", + "IAB-2.1", + "IAB-2.2", + "IAB-3.0" + ], + "description": "A valid taxonomy identifier recognized by MARS", + "example": "IAB-1.0" + }, + "categories": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "example": [ + "IAB1-5" + ] + } + }, + "required": [ + "taxonomy", + "categories" + ] + }, + "Consent": { + "type": "object", + "description": "An object to specify consent specifiers for this request", + "properties": { + "gpp": { + "type": "string", + "description": "Global Privacy Platform consent string" + } + } + }, + "AdCallbacks": { + "type": "object", + "description": "An object containing callback URLs for interactions with an ad.", + "properties": { + "click": { + "type": "string", + "description": "This URL should be requested with an HTTP GET when the ad is clicked. Response should be ignored." + }, + "impression": { + "type": "string", + "description": "This URL should be requested with an HTTP GET when half of the ad is visible in the viewport for 1 second. If the ad's pixel size is greater that 242500 (970 * 250) only 30% visibility is required. Response should be ignored." + }, + "report": { + "type": "string", + "description": "This URL may be issued by a client on behalf of a user to report an ad that is inappropriate or otherwise unsatisfying. Response should be ignored. The reason parameter is required with this action." + } + } + }, + "Attributions": { + "type": "object", + "description": "An object containing attribution configuration for enabled ads.", + "properties": { + "partner_id": { + "type": "string", + "format": "uuid", + "description": "Advertising partner associated with the ad." + }, + "index": { + "type": "number", + "description": "Index allocated to be used when a non-default report is sent." + } + }, + "required": [ + "partner_id", + "index" + ] + }, + "Task": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "DAP task ID.", + "example": "pL9iVhsLWNFb_W044YEbdOYC5y5_RZlPJZovrlk-8Vs" + }, + "vdaf": { + "type": "string", + "description": "DAP data type of the task.", + "example": "histogram" + }, + "bits": { + "type": "number", + "description": "DAP data size of the task" + }, + "length": { + "type": "number", + "description": "DAP legnth of the task.", + "example": 10 + }, + "time_precision": { + "type": "number", + "description": "DAP time precision. Determines rounding of dates in DAP report.", + "example": 3600 + }, + "default_measurement": { + "type": "number", + "description": "Measurement to be used when a default report is sent.", + "example": 0 + } + }, + "required": [ + "task_id", + "vdaf", + "length", + "time_precision" + ] + }, + "AdFormatBase": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The target destination URL of the ad." + }, + "callbacks": { + "$ref": "#/components/schemas/AdCallbacks" + }, + "attributions": { + "$ref": "#/components/schemas/Attributions" + } + }, + "required": [ + "url", + "callbacks" + ] + }, + "SpocFrequencyCaps": { + "type": "object", + "description": "Client-side enforced frequency capping information.", + "properties": { + "cap_key": { + "type": "string", + "description": "A key that identifies the frequency cap.", + "example": 345678901 + }, + "day": { + "type": "integer", + "format": "int32", + "description": "Number of times to show the same ad during a one day period.", + "example": 10 + } + }, + "required": [ + "cap_key", + "day" + ] + }, + "SpocRanking": { + "type": "object", + "description": "Ranking information for personalized content.", + "properties": { + "priority": { + "type": "integer", + "format": "int32", + "description": "The priority in the ranking. Reranking of ads should prefer priority before personalization.", + "example": 1 + }, + "personalization_models": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + }, + "description": "A map of model names to scores for personalization.", + "example": { + "arts_and_entertainment": 1, + "autos_and_vehicles": 1 + } + }, + "item_score": { + "type": "number", + "format": "float", + "description": "The overall score for the item.", + "example": 0.2 + } + }, + "required": [ + "priority", + "item_score" + ] + }, + "Image": { + "allOf": [ + { + "$ref": "#/components/schemas/AdFormatBase" + }, + { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "The format of the ad, indicating it is an image ad of a specific size. Values include `rectangle`, `billboard`, `skyscraper`, `leaderboard`.", + "example": "rectangle" + }, + "image_url": { + "type": "string", + "description": "URL of the ad image." + }, + "alt_text": { + "type": "string", + "description": "Alt text to describe the ad image.", + "example": "ACME Corp. Spring Launcher" + }, + "block_key": { + "type": "string", + "description": "The block key generated for the advertiser.", + "example": "CAISEm15IHNwZWNpYWwgc3BvbnNvcg" + } + }, + "required": [ + "format", + "image_url", + "block_key" + ] + } + ] + }, + "Spoc": { + "allOf": [ + { + "$ref": "#/components/schemas/AdFormatBase" + }, + { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "The format of the ad, `spoc`.", + "example": "spoc" + }, + "image_url": { + "type": "string", + "description": "URL of the ad image." + }, + "title": { + "type": "string", + "description": "Title of the sponsored content.", + "example": "Good news everyone!" + }, + "domain": { + "type": "string", + "description": "The domain where the content is hosted.", + "example": "example.com" + }, + "excerpt": { + "type": "string", + "description": "A short excerpt from the sponsored content.", + "example": "Read more about this..." + }, + "sponsor": { + "type": "string", + "description": "The name of the sponsor.", + "example": "ACME Corp" + }, + "sponsored_by_override": { + "type": "string", + "description": "An optional override for the sponsor name.", + "example": "Organized by ACME Corp" + }, + "block_key": { + "type": "string", + "description": "The block key generated for the advertiser.", + "example": "CAISEm15IHNwZWNpYWwgc3BvbnNvcg" + }, + "caps": { + "$ref": "#/components/schemas/SpocFrequencyCaps" + }, + "ranking": { + "$ref": "#/components/schemas/SpocRanking" + } + }, + "required": [ + "format", + "image_url", + "title", + "domain", + "excerpt", + "sponsor", + "block_key", + "caps", + "ranking" + ] + } + ] + }, + "Tile": { + "allOf": [ + { + "$ref": "#/components/schemas/AdFormatBase" + }, + { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "The format of the ad, `tile`.", + "example": "tile" + }, + "image_url": { + "type": "string", + "description": "URL of the ad image." + }, + "name": { + "type": "string", + "description": "The name displayed under the tile.", + "example": "Amazon" + }, + "block_key": { + "type": "string", + "description": "The block key generated for the advertiser.", + "example": "CAISEm15IHNwZWNpYWwgc3BvbnNvcg" + } + }, + "required": [ + "format", + "image_url", + "name", + "block_key" + ] + } + ] + }, + "AdResponse": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "$ref": "#/components/schemas/Spoc" + }, + { + "$ref": "#/components/schemas/Tile" + } + ] + } + } + }, + "TelemetryResponse": { + "type": "object", + "description": "An empty response object" + }, + "PreflightResponse": { + "type": "object", + "properties": { + "geoname_id": { + "type": "string", + "pattern": "^\\s*\\d+\\s*(,\\s*\\d+\\s*)*$", + "description": "A comma delimited list of geoname IDs representing the caller's location, based on their IP address. The first member of the list is the most specific location, with subsequent members being less specific locations.", + "example": "6077243,6252001" + }, + "geo_location": { + "type": "string", + "pattern": "^(\\d+)?,(\\w+)?,(\\w+)?$", + "description": "A structured representation of the caller's location, based on their IP address.", + "example": "506,MA,US" + }, + "normalized_ua": { + "type": "string", + "description": "A user agent string that will be similar to the caller's user agent string in characteristics, but will have some data normalized to make it less unique." + } + }, + "required": [ + "geoname_id", + "geo_location" + ] + } + } + } +} \ No newline at end of file diff --git a/components/ads-client/src/client/ad_request.rs b/components/ads-client/src/client/ad_request.rs index 031406d2cd..1ba5e2cffd 100644 --- a/components/ads-client/src/client/ad_request.rs +++ b/components/ads-client/src/client/ad_request.rs @@ -84,6 +84,7 @@ impl AdRequest { pub struct AdPlacementRequest { pub placement: String, pub count: u32, + #[serde(skip_serializing_if = "Option::is_none")] pub content: Option, } @@ -274,6 +275,90 @@ mod tests { assert_eq!(RequestHash::new(&req1), RequestHash::new(&req2)); } + /// Validate serialized requests against the MARS OpenAPI spec. + mod contract { + use super::*; + use crate::test_utils::{mars_schema, validate_against_mars_schema}; + use serde_json::to_value; + + fn ad_request_schema() -> serde_json::Value { + mars_schema("AdRequest") + } + + #[test] + fn test_minimal_request_validates_against_spec() { + let url: Url = "https://ads.mozilla.org/v1/ads".parse().unwrap(); + let request = AdRequest::try_new( + "decafbad-0cd1-0cd2-0cd3-decafbad1000".to_string(), + vec![AdPlacementRequest { + placement: "newtab_tile_1".to_string(), + count: 1, + content: None, + }], + url, + ) + .unwrap(); + + validate_against_mars_schema(&ad_request_schema(), &to_value(&request).unwrap()); + } + + #[test] + fn test_full_request_with_content_validates_against_spec() { + let url: Url = "https://ads.mozilla.org/v1/ads".parse().unwrap(); + let request = AdRequest::try_new( + "decafbad-0cd1-0cd2-0cd3-decafbad1000".to_string(), + vec![ + AdPlacementRequest { + placement: "newtab_tile_1".to_string(), + count: 2, + content: Some(AdContentCategory { + taxonomy: IABContentTaxonomy::IAB2_1, + categories: vec!["technology".to_string()], + }), + }, + AdPlacementRequest { + placement: "pocket_billboard".to_string(), + count: 1, + content: None, + }, + ], + url, + ) + .unwrap(); + + validate_against_mars_schema(&ad_request_schema(), &to_value(&request).unwrap()); + } + + #[test] + fn test_all_taxonomy_variants_validate_against_spec() { + let schema = ad_request_schema(); + let url: Url = "https://ads.mozilla.org/v1/ads".parse().unwrap(); + + for taxonomy in [ + IABContentTaxonomy::IAB1_0, + IABContentTaxonomy::IAB2_0, + IABContentTaxonomy::IAB2_1, + IABContentTaxonomy::IAB2_2, + IABContentTaxonomy::IAB3_0, + ] { + let request = AdRequest::try_new( + "decafbad-0cd1-0cd2-0cd3-decafbad1000".to_string(), + vec![AdPlacementRequest { + placement: "test".to_string(), + count: 1, + content: Some(AdContentCategory { + taxonomy, + categories: vec!["cat".to_string()], + }), + }], + url.clone(), + ) + .unwrap(); + validate_against_mars_schema(&schema, &to_value(&request).unwrap()); + } + } + } + #[test] fn test_different_placements_produce_different_hash() { use crate::http_cache::RequestHash; diff --git a/components/ads-client/src/client/ad_response.rs b/components/ads-client/src/client/ad_response.rs index 8cf72206fc..06cd7f3ab9 100644 --- a/components/ads-client/src/client/ad_response.rs +++ b/components/ads-client/src/client/ad_response.rs @@ -469,6 +469,176 @@ mod tests { .contains("request_hash=abc123def456")); } + /// Validate fixtures against the MARS OpenAPI spec, then deserialize into our types. + mod contract { + use super::*; + use crate::test_utils::{mars_schema, validate_against_mars_schema}; + use serde_json::json; + + #[test] + fn test_image_full_validates_and_deserializes() { + let schema = mars_schema("Image"); + let fixture = json!({ + "url": "https://example.com/landing", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression", + "report": "https://ads.mozilla.org/v1/t?report" + }, + "format": "billboard", + "image_url": "https://example.com/image.png", + "alt_text": "Example ad", + "block_key": "block-abc-123" + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("Image should deserialize from spec JSON"); + } + + #[test] + fn test_image_minimal_validates_and_deserializes() { + let schema = mars_schema("Image"); + let fixture = json!({ + "url": "https://example.com/landing", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression" + }, + "format": "rectangle", + "image_url": "https://example.com/image.png", + "block_key": "block-abc-123" + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("Image should deserialize without optional fields"); + } + + // Spec marks click/impression as optional; we require them so ads + // without callbacks are dropped during deserialization. + #[test] + fn test_image_missing_click_is_spec_valid_but_fails_our_deserialization() { + let schema = mars_schema("Image"); + let fixture = json!({ + "url": "https://example.com/landing", + "callbacks": { "impression": "https://ads.mozilla.org/v1/t?impression" }, + "format": "rectangle", + "image_url": "https://example.com/image.png", + "block_key": "block-abc-123" + }); + validate_against_mars_schema(&schema, &fixture); + assert!(serde_json::from_value::(fixture).is_err()); + } + + #[test] + fn test_image_missing_impression_is_spec_valid_but_fails_our_deserialization() { + let schema = mars_schema("Image"); + let fixture = json!({ + "url": "https://example.com/landing", + "callbacks": { "click": "https://ads.mozilla.org/v1/t?click" }, + "format": "rectangle", + "image_url": "https://example.com/image.png", + "block_key": "block-abc-123" + }); + validate_against_mars_schema(&schema, &fixture); + assert!(serde_json::from_value::(fixture).is_err()); + } + + // We don't model attributions; serde ignores unknown fields. + #[test] + fn test_image_with_attributions_validates_and_deserializes() { + let schema = mars_schema("Image"); + let fixture = json!({ + "url": "https://example.com/landing", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression" + }, + "format": "billboard", + "image_url": "https://example.com/image.png", + "block_key": "block-abc-123", + "attributions": { + "partner_id": "00000000-0000-0000-0000-000000000001", + "index": 0 + } + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("attributions field should be ignored"); + } + + #[test] + fn test_spoc_full_validates_and_deserializes() { + let schema = mars_schema("Spoc"); + let fixture = json!({ + "url": "https://example.com/article", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression" + }, + "format": "spoc", + "image_url": "https://example.com/image.png", + "title": "Sponsored article title", + "domain": "example.com", + "excerpt": "A short excerpt of the sponsored content.", + "sponsor": "Example Sponsor", + "sponsored_by_override": "Override text", + "block_key": "block-spoc-123", + "caps": { "cap_key": "example-cap", "day": 3 }, + "ranking": { + "priority": 1, + "personalization_models": { "model_a": 42 }, + "item_score": 0.95 + } + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("Spoc should deserialize from spec JSON"); + } + + #[test] + fn test_spoc_minimal_validates_and_deserializes() { + let schema = mars_schema("Spoc"); + let fixture = json!({ + "url": "https://example.com/article", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression" + }, + "format": "spoc", + "image_url": "https://example.com/image.png", + "title": "Sponsored article title", + "domain": "example.com", + "excerpt": "A short excerpt.", + "sponsor": "Example Sponsor", + "block_key": "block-spoc-123", + "caps": { "cap_key": "example-cap", "day": 3 }, + "ranking": { "priority": 1, "item_score": 0.5 } + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("Spoc should deserialize without optional fields"); + } + + #[test] + fn test_tile_validates_and_deserializes() { + let schema = mars_schema("Tile"); + let fixture = json!({ + "url": "https://example.com", + "callbacks": { + "click": "https://ads.mozilla.org/v1/t?click", + "impression": "https://ads.mozilla.org/v1/t?impression" + }, + "format": "tile", + "image_url": "https://example.com/logo.png", + "name": "Example Site", + "block_key": "block-tile-123" + }); + validate_against_mars_schema(&schema, &fixture); + serde_json::from_value::(fixture) + .expect("Tile should deserialize from spec JSON"); + } + } + #[test] fn test_pop_request_hash_from_url() { let mut url_with_hash = diff --git a/components/ads-client/src/test_utils.rs b/components/ads-client/src/test_utils.rs index 6e5407da66..5669058e74 100644 --- a/components/ads-client/src/test_utils.rs +++ b/components/ads-client/src/test_utils.rs @@ -14,6 +14,54 @@ use crate::client::{ }, }; +const OPENAPI_JSON: &str = include_str!("../openapi.json"); + +/// Load a named schema from the MARS OpenAPI spec with `$ref` pointers resolved. +pub fn mars_schema(name: &str) -> serde_json::Value { + let root: serde_json::Value = + serde_json::from_str(OPENAPI_JSON).expect("openapi.json should parse"); + let schema = root["components"]["schemas"][name].clone(); + assert!( + !schema.is_null(), + "Schema '{name}' not found in openapi.json" + ); + resolve_refs(&schema, &root) +} + +pub fn validate_against_mars_schema(schema: &serde_json::Value, instance: &serde_json::Value) { + let validator = jsonschema::validator_for(schema).expect("schema should compile"); + if let Err(e) = validator.validate(instance) { + panic!( + "JSON Schema validation failed:\n {e}\nInstance:\n{}", + serde_json::to_string_pretty(instance).unwrap() + ); + } +} + +fn resolve_refs(value: &serde_json::Value, root: &serde_json::Value) -> serde_json::Value { + match value { + serde_json::Value::Object(map) => { + if let Some(serde_json::Value::String(ref_path)) = map.get("$ref") { + let pointer = ref_path.trim_start_matches('#'); + let resolved = root + .pointer(pointer) + .unwrap_or_else(|| panic!("$ref '{ref_path}' not found in openapi.json")); + resolve_refs(resolved, root) + } else { + let new_map: serde_json::Map = map + .iter() + .map(|(k, v)| (k.clone(), resolve_refs(v, root))) + .collect(); + serde_json::Value::Object(new_map) + } + } + serde_json::Value::Array(arr) => { + serde_json::Value::Array(arr.iter().map(|v| resolve_refs(v, root)).collect()) + } + other => other.clone(), + } +} + pub const TEST_CONTEXT_ID: &str = "00000000-0000-4000-8000-000000000001"; pub fn make_happy_placement_requests() -> Vec { diff --git a/components/ads-client/tests/integration_test.rs b/components/ads-client/tests/integration_test.rs index b73d8fa542..2f2620b429 100644 --- a/components/ads-client/tests/integration_test.rs +++ b/components/ads-client/tests/integration_test.rs @@ -8,12 +8,13 @@ use std::time::Duration; use ads_client::{ http_cache::{ByteSize, CacheOutcome, HttpCache, RequestCachePolicy}, - MozAdsClientBuilder, MozAdsPlacementRequest, MozAdsPlacementRequestWithCount, + MozAdsClientBuilder, MozAdsEnvironment, MozAdsPlacementRequest, + MozAdsPlacementRequestWithCount, }; +use std::sync::Arc; use url::Url; use viaduct::Request; -/// Test-only hashable wrapper around Request. #[derive(Clone)] struct TestRequest(Request); @@ -30,102 +31,82 @@ impl From for Request { } } -#[test] -#[ignore] -fn test_mock_pocket_billboard_1_placement() { - viaduct_dev::init_backend_dev(); - - let client = MozAdsClientBuilder::new().build(); - - let placement_request = MozAdsPlacementRequest { - placement_id: "mock_pocket_billboard_1".to_string(), - iab_content: None, - }; - - let result = client.request_image_ads(vec![placement_request], None); - - assert!(result.is_ok(), "Failed to request ads: {:?}", result.err()); - - let placements = result.unwrap(); - - assert!( - placements.contains_key("mock_pocket_billboard_1"), - "Response should contain placement_id 'mock_pocket_billboard_1'" - ); +fn init_backend() { + let _ = viaduct_hyper::viaduct_init_backend_hyper(); +} - placements - .get("mock_pocket_billboard_1") - .expect("Placement should exist"); +fn staging_client() -> ads_client::MozAdsClient { + Arc::new(MozAdsClientBuilder::new()) + .environment(MozAdsEnvironment::Staging) + .build() } #[test] -#[ignore] -fn test_newtab_spocs_placement() { - viaduct_dev::init_backend_dev(); - - let client = MozAdsClientBuilder::new().build(); - - let count = 3; - let placement_request = MozAdsPlacementRequestWithCount { - placement_id: "newtab_spocs".to_string(), - count, - iab_content: None, - }; - - let result = client.request_spoc_ads(vec![placement_request], None); - - assert!(result.is_ok(), "Failed to request ads: {:?}", result.err()); - - let placements = result.unwrap(); - - assert!( - placements.contains_key("newtab_spocs"), - "Response should contain placement_id 'newtab_spocs'" +#[ignore = "contract test: hits MARS staging"] +fn test_contract_image_staging() { + init_backend(); + + let client = staging_client(); + let result = client.request_image_ads( + vec![MozAdsPlacementRequest { + placement_id: "mock_billboard_1".to_string(), + iab_content: None, + }], + None, ); - let spocs = placements - .get("newtab_spocs") - .expect("Placement should exist"); - - assert_eq!( - spocs.len(), - count as usize, - "Number of spocs should equal count parameter" + assert!( + result.is_ok(), + "Image ad request failed: {:?}", + result.err() ); + let placements = result.unwrap(); + assert!(placements.contains_key("mock_billboard_1")); } #[test] -#[ignore] -fn test_newtab_tile_1_placement() { - viaduct_dev::init_backend_dev(); - - let client = MozAdsClientBuilder::new().build(); - - let placement_request = MozAdsPlacementRequest { - placement_id: "newtab_tile_1".to_string(), - iab_content: None, - }; - - let result = client.request_tile_ads(vec![placement_request], None); - - assert!(result.is_ok(), "Failed to request ads: {:?}", result.err()); +#[ignore = "contract test: hits MARS staging"] +fn test_contract_spoc_staging() { + init_backend(); + + let client = staging_client(); + let result = client.request_spoc_ads( + vec![MozAdsPlacementRequestWithCount { + placement_id: "mock_spoc_1".to_string(), + count: 1, + iab_content: None, + }], + None, + ); + assert!(result.is_ok(), "Spoc ad request failed: {:?}", result.err()); let placements = result.unwrap(); + assert!(placements.contains_key("mock_spoc_1")); +} - assert!( - placements.contains_key("newtab_tile_1"), - "Response should contain placement_id 'newtab_tile_1'" +#[test] +#[ignore = "contract test: hits MARS staging"] +fn test_contract_tile_staging() { + init_backend(); + + let client = staging_client(); + let result = client.request_tile_ads( + vec![MozAdsPlacementRequest { + placement_id: "mock_tile_1".to_string(), + iab_content: None, + }], + None, ); - placements - .get("newtab_tile_1") - .expect("Placement should exist"); + assert!(result.is_ok(), "Tile ad request failed: {:?}", result.err()); + let placements = result.unwrap(); + assert!(placements.contains_key("mock_tile_1")); } #[test] #[ignore] fn test_cache_works_using_real_timeouts() { - viaduct_dev::init_backend_dev(); + init_backend(); let cache: HttpCache = HttpCache::builder("integration_tests.db") .default_ttl(Duration::from_secs(60)) @@ -138,7 +119,7 @@ fn test_cache_works_using_real_timeouts() { "context_id": "12347fff-00b0-aaaa-0978-189231239808", "placements": [ { - "placement": "mock_pocket_billboard_1", + "placement": "mock_billboard_1", "count": 1, } ],