Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
106 changes: 93 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Run Tests"
name: 'Run Tests'

permissions:
contents: read
Expand All @@ -18,19 +18,19 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Retrieve Viceroy version
id: viceroy-version
# `.tool-versions` is the single source of truth so this workflow and
# `.github/actions/setup-integration-test-env/action.yml` can't drift.
run: echo "viceroy-version=$(grep '^viceroy ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "viceroy-version=$(grep '^viceroy ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Retrieve Node.js version
id: node-version
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain
Expand Down Expand Up @@ -71,6 +71,65 @@ jobs:
- name: Run cache purge harness
run: BID_DELAY=3 ./scripts/template-cache-local-test.sh purge

test-fastly-eof:
name: HTML EOF runtime (Fastly)
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4

- name: Read pinned build tool versions
id: versions
shell: bash
run: |
echo "rust=$(awk '$1 == "rust" { print $2 }' .tool-versions)" >> "$GITHUB_OUTPUT"
echo "node=$(awk '$1 == "nodejs" { print $2 }' .tool-versions)" >> "$GITHUB_OUTPUT"

- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ steps.versions.outputs.rust }}
target: wasm32-wasip1
cache-shared-key: cargo-${{ runner.os }}

- uses: actions/setup-node@v4
with:
node-version: ${{ steps.versions.outputs.node }}

- name: Cache isolated EOF fixture Viceroy
id: cache-eof-viceroy
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/viceroy-eof/bin/viceroy
key: viceroy-eof-${{ runner.os }}-amd64-v0.19.0-sha256-23b2af7d

- name: Install isolated Viceroy 0.19 for backend timeout simulation
if: steps.cache-eof-viceroy.outputs.cache-hit != 'true'
shell: bash
run: |
curl -fsSL --retry 3 https://github.com/fastly/Viceroy/releases/download/v0.19.0/viceroy_v0.19.0_linux-amd64.tar.gz -o "$RUNNER_TEMP/viceroy-eof.tar.gz"
echo "23b2af7d8dce8f4a217716d6bd8b15c4f41494316a0d4e80f179ffb4b6c74744 $RUNNER_TEMP/viceroy-eof.tar.gz" | sha256sum --check --strict
mkdir -p "$RUNNER_TEMP/viceroy-eof/bin"
tar -xzf "$RUNNER_TEMP/viceroy-eof.tar.gz" -C "$RUNNER_TEMP/viceroy-eof/bin" viceroy

- name: Select isolated fixture runtime
shell: bash
run: echo "$RUNNER_TEMP/viceroy-eof/bin" >> "$GITHUB_PATH"

- name: Verify actual Fastly HTML EOF completion and auction assembly
timeout-minutes: 15
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
TMPDIR: ${{ runner.temp }}
run: ./scripts/async-html-finalization-local-test.sh fastly --release --auction

- name: Upload EOF runtime diagnostics on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: fastly-html-eof-diagnostics
path: ${{ runner.temp }}/trusted-server-finalize-fastly-*/
if-no-files-found: ignore

test-axum:
name: cargo test (axum native)
runs-on: ubuntu-latest
Expand All @@ -79,7 +138,7 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain
Expand All @@ -106,6 +165,27 @@ jobs:
- name: Run Axum adapter tests
run: cargo test-axum

- name: Verify finalization harness process cleanup
run: cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --features html-eof-runtime --test html_eof_runtime

- name: Lint native finalization harness
run: cargo clippy --manifest-path crates/trusted-server-integration-tests/Cargo.toml --features html-eof-runtime --test html_eof_runtime -- -D warnings

- name: Verify actual Axum HTML finalization and auction assembly
timeout-minutes: 10
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
TMPDIR: ${{ runner.temp }}
run: ./scripts/async-html-finalization-local-test.sh axum --auction

- name: Upload finalization runtime diagnostics on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: axum-html-finalization-diagnostics
path: ${{ runner.temp }}/trusted-server-finalize-axum-*/
if-no-files-found: ignore

- name: Test template build digest inputs (native)
run: cargo test -p trusted-server-core --test template_build_digest

Expand Down Expand Up @@ -133,7 +213,7 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain (native + wasm32-unknown-unknown)
Expand All @@ -160,7 +240,7 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain (native + wasm32-wasip1)
Expand All @@ -185,7 +265,7 @@ jobs:
TRUSTED_SERVER__PUBLISHER__ORIGIN_URL: http://127.0.0.1:8080
TRUSTED_SERVER__PUBLISHER__PROXY_SECRET: integration-test-proxy-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret-padded-32
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: "false"
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: 'false'
run: cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release

- name: Run Spin adapter tests (native host)
Expand All @@ -199,7 +279,7 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain
Expand Down Expand Up @@ -230,14 +310,14 @@ jobs:

- name: Retrieve Rust version
id: rust-version
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Set up Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ steps.rust-version.outputs.rust-version }}
components: "clippy, rustfmt"
components: 'clippy, rustfmt'
cache-shared-key: cargo-cli-${{ runner.os }}

- name: Read pinned Node.js version for CLI fixtures
Expand Down Expand Up @@ -304,14 +384,14 @@ jobs:
- name: Retrieve Node.js version
id: node-version
working-directory: .
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.node-version }}
cache: "npm"
cache: 'npm'
cache-dependency-path: crates/trusted-server-js/lib/package.json

- name: Install dependencies
Expand Down
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ Every PR must pass:
8. Markdown format outside `docs/` (requires `cd docs && npm ci` first): `docs/node_modules/.bin/prettier --config docs/.prettierrc --check "*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md" "tinybird/**/*.md"`; fix with `--write` in place of `--check`
9. Native core build-digest test and lint (`cargo test -p trusted-server-core --test template_build_digest` and `cargo clippy -p trusted-server-core --test template_build_digest -- -D warnings`)
10. Wasm clock lint: `scripts/lint-wasm-clock.sh` (crates built into the Cloudflare Worker must use `web_time`, not the `std::time::{Instant,SystemTime}` `now`/`elapsed` methods, which panic there)
11. Native Rust HTML EOF harness configuration, timing and cleanup: `cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --features html-eof-runtime --test html_eof_runtime` and `cargo clippy --manifest-path crates/trusted-server-integration-tests/Cargo.toml --features html-eof-runtime --test html_eof_runtime -- -D warnings`
12. Actual Axum EOF/auction controls: `./scripts/async-html-finalization-local-test.sh axum --auction`
13. Actual Fastly EOF/auction controls with isolated Viceroy 0.19+: `PATH="/tmp/example-eof-viceroy/bin:$PATH" ./scripts/async-html-finalization-local-test.sh fastly --release --auction`; install with `cargo install viceroy --version 0.19.0 --locked --root /tmp/example-eof-viceroy`. Ordinary tests retain pinned Viceroy 0.17. See `scripts/README.md` for retained diagnostics and other runtimes.

---

Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/trusted-server-adapter-axum/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ async fn dispatch_fallback(
// slots; `handle_publisher_request` matches them against the request path.
let mut ec_context = build_ec_context(state, services, &req);
let auction = AuctionDispatch {
integration_registry: &state.registry,
orchestrator: &state.orchestrator,
slots: state.settings.creative_opportunity_slots(),
registry: None,
Expand Down Expand Up @@ -526,6 +527,7 @@ fn named_route_handler(
} else {
let mut ec_context = build_ec_context(&state, &services, &req);
let auction = AuctionDispatch {
integration_registry: &state.registry,
orchestrator: &state.orchestrator,
slots: state.settings.creative_opportunity_slots(),
registry: None,
Expand Down
24 changes: 24 additions & 0 deletions crates/trusted-server-adapter-axum/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ pub fn build_runtime_services(ctx: &edgezero_core::context::RequestContext) -> R
static GEO: std::sync::OnceLock<Arc<dyn PlatformGeo>> = std::sync::OnceLock::new();

RuntimeServices::builder()
.timer(Arc::new(AxumTimer))
.config_store(Arc::clone(CONFIG_STORE.get_or_init(|| {
Arc::new(AxumPlatformConfigStore) as Arc<dyn PlatformConfigStore>
})))
Expand Down Expand Up @@ -581,12 +582,35 @@ pub fn build_runtime_services(ctx: &edgezero_core::context::RequestContext) -> R
.build()
}

struct AxumTimer;

impl trusted_server_core::platform::PlatformTimer for AxumTimer {
fn delay(&self, duration: std::time::Duration) -> futures::future::LocalBoxFuture<'static, ()> {
Box::pin(async move {
tokio::time::sleep(duration).await;
})
}
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
#[test]
fn html_finalization_timer_is_request_scoped() {
let request = edgezero_core::http::request_builder()
.body(edgezero_core::body::Body::empty())
.expect("should build a request");
let context = edgezero_core::context::RequestContext::new(request, Default::default());
let services = build_runtime_services(&context);
assert!(
services.timer().is_some(),
"should install a cooperative timer without polling it"
);
}

use super::*;
use edgezero_core::body::Body as EdgeBody;
use std::time::Duration;
Expand Down
2 changes: 2 additions & 0 deletions crates/trusted-server-adapter-cloudflare/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ crate-type = ["cdylib", "rlib"]

[features]
default = []
# Opt-in local runtime fixture and its diagnostic console sink.
test-utils = ["trusted-server-core/test-utils"]
# Keep for explicit `cargo check --features cloudflare --target wasm32-unknown-unknown`
cloudflare = ["edgezero-adapter-cloudflare/cloudflare", "dep:worker"]

Expand Down
2 changes: 2 additions & 0 deletions crates/trusted-server-adapter-cloudflare/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ fn build_router(state: &Arc<AppState>) -> RouterService {
} else {
let mut ec_context = build_ec_context(&state.settings, &services, &req);
let auction = AuctionDispatch {
integration_registry: &state.registry,
orchestrator: &state.orchestrator,
slots: state.settings.creative_opportunity_slots(),
registry: None,
Expand Down Expand Up @@ -724,6 +725,7 @@ fn build_router(state: &Arc<AppState>) -> RouterService {
let page_bids = make_handler(Arc::clone(&state), |s, services, req| async move {
let mut ec_context = build_ec_context(&s.settings, &services, &req);
let auction = AuctionDispatch {
integration_registry: &s.registry,
orchestrator: &s.orchestrator,
slots: s.settings.creative_opportunity_slots(),
registry: None,
Expand Down
31 changes: 31 additions & 0 deletions crates/trusted-server-adapter-cloudflare/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use worker::{Context, Env, Request, Response, Result, event};
/// Returns a Workers runtime error when the fallback error response cannot be
/// constructed.
pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
#[cfg(feature = "test-utils")]
install_fixture_logger();
if let Ok(config) = env.var("TRUSTED_SERVER_CONFIG") {
app::set_cloudflare_config_json(config.to_string());
}
Expand All @@ -37,3 +39,32 @@ pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
}
}
}

#[cfg(all(feature = "test-utils", target_arch = "wasm32"))]
struct FixtureLogger;

#[cfg(all(feature = "test-utils", target_arch = "wasm32"))]
impl log::Log for FixtureLogger {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
metadata.level() <= log::Level::Warn
}

fn log(&self, record: &log::Record<'_>) {
if self.enabled(record.metadata()) {
worker::console_warn!("{}", record.args());
}
}

fn flush(&self) {}
}

#[cfg(all(feature = "test-utils", target_arch = "wasm32"))]
fn install_fixture_logger() {
static LOGGER: FixtureLogger = FixtureLogger;
if log::set_logger(&LOGGER).is_ok() {
std::panic::set_hook(Box::new(|information| {
worker::console_error!("{}", information);
}));
log::set_max_level(log::LevelFilter::Warn);
}
}
Loading
Loading