Skip to content

Commit 2d2600e

Browse files
committed
Feature integration test with TestContainers
1 parent ac612b2 commit 2d2600e

24 files changed

Lines changed: 1294 additions & 186 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: "Integration Tests"
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
push:
8+
branches: [main]
9+
pull_request:
10+
branches: [main]
11+
paths:
12+
- "crates/integration-tests/**"
13+
- "crates/common/**"
14+
- "crates/fastly/**"
15+
- ".github/workflows/integration-tests.yml"
16+
17+
jobs:
18+
integration-tests:
19+
name: integration tests
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Retrieve Rust version
25+
id: rust-version
26+
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
27+
shell: bash
28+
29+
- name: Set up Rust toolchain
30+
uses: actions-rust-lang/setup-rust-toolchain@v1
31+
with:
32+
toolchain: ${{ steps.rust-version.outputs.rust-version }}
33+
target: wasm32-wasip1
34+
cache-shared-key: cargo-${{ runner.os }}
35+
36+
- name: Get Viceroy cache key
37+
id: viceroy-rev
38+
run: echo "sha=$(git ls-remote https://github.com/fastly/Viceroy HEAD | cut -f1)" >> $GITHUB_OUTPUT
39+
40+
- name: Cache Viceroy binary
41+
id: cache-viceroy
42+
uses: actions/cache@v4
43+
with:
44+
path: ~/.cargo/bin/viceroy
45+
key: viceroy-${{ runner.os }}-${{ steps.viceroy-rev.outputs.sha }}
46+
47+
- name: Install Viceroy
48+
if: steps.cache-viceroy.outputs.cache-hit != 'true'
49+
run: cargo install --git https://github.com/fastly/Viceroy viceroy
50+
51+
- name: Build WASM binary
52+
run: cargo build --bin trusted-server-fastly --release --target wasm32-wasip1
53+
54+
- name: Build WordPress test container
55+
run: |
56+
docker build -t test-wordpress:latest \
57+
crates/integration-tests/fixtures/frameworks/wordpress/
58+
59+
- name: Build Next.js test container
60+
run: |
61+
docker build -t test-nextjs:latest \
62+
crates/integration-tests/fixtures/frameworks/nextjs/
63+
64+
- name: Run integration tests
65+
run: cargo test -p integration-tests --target x86_64-unknown-linux-gnu
66+
env:
67+
WASM_BINARY_PATH: target/wasm32-wasip1/release/trusted-server-fastly.wasm
68+
RUST_LOG: info

INTEGRATION_TESTS_PLAN.md

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -76,77 +76,73 @@ Track progress for each phase using this checklist:
7676
- [x] Start Viceroy manually to test health endpoint
7777
- [x] Verify `curl http://127.0.0.1:7676/health` returns 200 OK
7878

79-
### Phase 1: Core Infrastructure ⏱️ 4-6 hours
80-
- [ ] Add `integration-tests` to workspace members in root `Cargo.toml`
81-
- [ ] Create [`crates/integration-tests/Cargo.toml`](crates/integration-tests/Cargo.toml) with dependencies
82-
- [ ] Create [`tests/common/mod.rs`](crates/integration-tests/tests/common/mod.rs) (re-exports)
83-
- [ ] Define `RuntimeEnvironment` trait in [`tests/common/runtime.rs`](crates/integration-tests/tests/common/runtime.rs)
84-
- [ ] Implement `RuntimeConfig` struct with platform-agnostic interface
85-
- [ ] Create [`tests/common/config.rs`](crates/integration-tests/tests/common/config.rs) for config generation
86-
- [ ] Implement assertion helpers in [`tests/common/assertions.rs`](crates/integration-tests/tests/common/assertions.rs)
87-
- [ ] Create [`fixtures/configs/fastly-template.toml`](crates/integration-tests/fixtures/configs/fastly-template.toml)
88-
- [ ] Verify: `cargo check -p integration-tests` passes
89-
90-
### Phase 1.5: Fastly Runtime Implementation ⏱️ 2-3 hours
91-
- [ ] Create [`tests/environments/mod.rs`](crates/integration-tests/tests/environments/mod.rs) with `RuntimeEnvironment` trait
92-
- [ ] Create [`tests/environments/fastly.rs`](crates/integration-tests/tests/environments/fastly.rs)
93-
- [ ] Implement `FastlyViceroy` struct with `RuntimeEnvironment` trait
94-
- [ ] Implement `ViceroyHandle` for process lifecycle (spawn, kill, wait)
95-
- [ ] Implement dynamic port allocation with `find_available_port()`
96-
- [ ] Implement health check with retry logic (30 attempts × 100ms)
97-
- [ ] Create `RUNTIME_ENVIRONMENTS` registry with Fastly factory
98-
- [ ] Verify: `cargo test -p integration-tests --lib` compiles
99-
100-
### Phase 2: Framework Abstraction ⏱️ 2-3 hours
101-
- [ ] Create [`tests/frameworks/mod.rs`](crates/integration-tests/tests/frameworks/mod.rs) with `FrontendFramework` trait
102-
- [ ] Create [`tests/frameworks/scenarios.rs`](crates/integration-tests/tests/frameworks/scenarios.rs)
103-
- [ ] Define `TestScenario` enum (HtmlInjection, ScriptServing, AttributeRewriting, GdprSignal)
104-
- [ ] Define `CustomScenario` enum (framework-specific scenarios)
105-
- [ ] Implement scenario runners with error context
106-
- [ ] Create `FRAMEWORKS` registry (empty initially)
107-
- [ ] Verify: Trait compiles, registry pattern works
108-
109-
### Phase 3: WordPress Implementation ⏱️ 5-7 hours
110-
- [ ] Create [`fixtures/frameworks/wordpress/`](crates/integration-tests/fixtures/frameworks/wordpress/) directory
111-
- [ ] Create [`fixtures/frameworks/wordpress/Dockerfile`](crates/integration-tests/fixtures/frameworks/wordpress/Dockerfile) with SQLite plugin
112-
- [ ] Create minimal WordPress test theme in [`fixtures/frameworks/wordpress/theme/`](crates/integration-tests/fixtures/frameworks/wordpress/theme/)
113-
- [ ] Build WordPress Docker image: `docker build -t test-wordpress:latest fixtures/frameworks/wordpress/`
114-
- [ ] Create [`tests/frameworks/wordpress.rs`](crates/integration-tests/tests/frameworks/wordpress.rs)
115-
- [ ] Implement `WordPress` struct with `FrontendFramework` trait
116-
- [ ] Add WordPress to `FRAMEWORKS` registry
117-
- [ ] Create [`tests/integration.rs`](crates/integration-tests/tests/integration.rs) with `test_combination()` helper
118-
- [ ] Create `test_wordpress_fastly()` test
119-
- [ ] Verify: `cargo test -p integration-tests -- test_wordpress_fastly` passes
120-
121-
### Phase 4: Next.js Implementation ⏱️ 3-4 hours
122-
- [ ] Create [`fixtures/frameworks/nextjs/`](crates/integration-tests/fixtures/frameworks/nextjs/) directory
123-
- [ ] Create [`fixtures/frameworks/nextjs/package.json`](crates/integration-tests/fixtures/frameworks/nextjs/package.json)
124-
- [ ] Create [`fixtures/frameworks/nextjs/next.config.mjs`](crates/integration-tests/fixtures/frameworks/nextjs/next.config.mjs)
125-
- [ ] Create [`fixtures/frameworks/nextjs/app/page.tsx`](crates/integration-tests/fixtures/frameworks/nextjs/app/page.tsx) with test content
126-
- [ ] Create [`fixtures/frameworks/nextjs/Dockerfile`](crates/integration-tests/fixtures/frameworks/nextjs/Dockerfile)
127-
- [ ] Build Next.js Docker image: `docker build -t test-nextjs:latest fixtures/frameworks/nextjs/`
128-
- [ ] Create [`tests/frameworks/nextjs.rs`](crates/integration-tests/tests/frameworks/nextjs.rs)
129-
- [ ] Implement `NextJs` struct with RSC-specific scenarios
130-
- [ ] Add Next.js to `FRAMEWORKS` registry
131-
- [ ] Create `test_nextjs_fastly()` test
132-
- [ ] Create `test_all_frameworks()` test (iterates `FRAMEWORKS` registry)
133-
- [ ] Verify: `cargo test -p integration-tests` passes for all tests
134-
135-
### Phase 5: Documentation and CI ⏱️ 3-4 hours
136-
- [ ] Create comprehensive [`crates/integration-tests/README.md`](crates/integration-tests/README.md)
137-
- [ ] Prerequisites section (Docker, Rust, Viceroy)
138-
- [ ] Running tests locally guide
139-
- [ ] How to add a new framework guide
140-
- [ ] How to add a new runtime guide (for future)
141-
- [ ] Troubleshooting section
142-
- [ ] Create [`.github/workflows/integration-tests.yml`](.github/workflows/integration-tests.yml)
143-
- [ ] Build WASM binary step
144-
- [ ] Build Docker images (WordPress, Next.js)
145-
- [ ] Install Viceroy
146-
- [ ] Run integration tests with WASM_BINARY_PATH env var
147-
- [ ] Create [`crates/integration-tests/.dockerignore`](crates/integration-tests/.dockerignore)
148-
- [ ] Update root [`Cargo.toml`](Cargo.toml) workspace members
149-
- [ ] Test CI locally with `act` or similar
79+
### Phase 1: Core Infrastructure ⏱️ 4-6 hours ✅ COMPLETE
80+
- [x] Add `integration-tests` to workspace members in root `Cargo.toml`
81+
- [x] Create [`crates/integration-tests/Cargo.toml`](crates/integration-tests/Cargo.toml) with dependencies
82+
- [x] Create [`tests/common/mod.rs`](crates/integration-tests/tests/common/mod.rs) (re-exports)
83+
- [x] Define `RuntimeEnvironment` trait in [`tests/common/runtime.rs`](crates/integration-tests/tests/common/runtime.rs)
84+
- [x] Implement `RuntimeConfig` struct with platform-agnostic interface
85+
- [x] Create [`tests/common/config.rs`](crates/integration-tests/tests/common/config.rs) for config generation
86+
- [x] Implement assertion helpers in [`tests/common/assertions.rs`](crates/integration-tests/tests/common/assertions.rs)
87+
- [x] Create [`fixtures/configs/fastly-template.toml`](crates/integration-tests/fixtures/configs/fastly-template.toml)
88+
- [x] Create [`fixtures/configs/viceroy-template.toml`](crates/integration-tests/fixtures/configs/viceroy-template.toml)
89+
- [x] Verify: `cargo check -p integration-tests` passes
90+
- [x] Verify: 9/9 assertion unit tests pass
91+
92+
### Phase 1.5: Fastly Runtime Implementation ⏱️ 2-3 hours ✅ COMPLETE
93+
- [x] Create [`tests/environments/mod.rs`](crates/integration-tests/tests/environments/mod.rs) with `RuntimeEnvironment` trait
94+
- [x] Create [`tests/environments/fastly.rs`](crates/integration-tests/tests/environments/fastly.rs)
95+
- [x] Implement `FastlyViceroy` struct with `RuntimeEnvironment` trait
96+
- [x] Implement `ViceroyHandle` for process lifecycle (spawn, kill, wait, Drop)
97+
- [x] Implement dynamic port allocation with `find_available_port()`
98+
- [x] Implement health check with retry logic (30 attempts × 100ms)
99+
- [x] Create `RUNTIME_ENVIRONMENTS` registry with Fastly factory
100+
- [x] Verify: test binary compiles for native target
101+
102+
### Phase 2: Framework Abstraction ⏱️ 2-3 hours ✅ COMPLETE
103+
- [x] Create [`tests/frameworks/mod.rs`](crates/integration-tests/tests/frameworks/mod.rs) with `FrontendFramework` trait
104+
- [x] Create [`tests/frameworks/scenarios.rs`](crates/integration-tests/tests/frameworks/scenarios.rs)
105+
- [x] Define `TestScenario` enum (HtmlInjection, ScriptServing, AttributeRewriting, GdprSignal)
106+
- [x] Define `CustomScenario` enum (WordPressAdminInjection, NextJsRscFlight, NextJsServerActions)
107+
- [x] Implement scenario runners with error context
108+
- [x] Create `FRAMEWORKS` registry with WordPress and Next.js factories
109+
- [x] Verify: Trait compiles, registry pattern works
110+
111+
### Phase 3: WordPress Implementation ⏱️ 5-7 hours ✅ COMPLETE
112+
- [x] Create [`fixtures/frameworks/wordpress/`](crates/integration-tests/fixtures/frameworks/wordpress/) directory
113+
- [x] Create [`fixtures/frameworks/wordpress/Dockerfile`](crates/integration-tests/fixtures/frameworks/wordpress/Dockerfile) (PHP CLI with test theme)
114+
- [x] Create minimal WordPress test theme in [`fixtures/frameworks/wordpress/theme/`](crates/integration-tests/fixtures/frameworks/wordpress/theme/)
115+
- [x] Create wp-admin test page for admin injection scenario
116+
- [x] Create [`tests/frameworks/wordpress.rs`](crates/integration-tests/tests/frameworks/wordpress.rs)
117+
- [x] Implement `WordPress` struct with `FrontendFramework` trait
118+
- [x] Add WordPress to `FRAMEWORKS` registry
119+
- [x] Create [`tests/integration.rs`](crates/integration-tests/tests/integration.rs) with `test_combination()` helper
120+
- [x] Create `test_wordpress_fastly()` test
121+
- [ ] Verify: `cargo test -p integration-tests -- test_wordpress_fastly` passes (requires Docker)
122+
123+
### Phase 4: Next.js Implementation ⏱️ 3-4 hours ✅ COMPLETE
124+
- [x] Create [`fixtures/frameworks/nextjs/`](crates/integration-tests/fixtures/frameworks/nextjs/) directory
125+
- [x] Create [`fixtures/frameworks/nextjs/package.json`](crates/integration-tests/fixtures/frameworks/nextjs/package.json)
126+
- [x] Create [`fixtures/frameworks/nextjs/next.config.mjs`](crates/integration-tests/fixtures/frameworks/nextjs/next.config.mjs) (standalone output)
127+
- [x] Create [`fixtures/frameworks/nextjs/app/layout.tsx`](crates/integration-tests/fixtures/frameworks/nextjs/app/layout.tsx)
128+
- [x] Create [`fixtures/frameworks/nextjs/app/page.tsx`](crates/integration-tests/fixtures/frameworks/nextjs/app/page.tsx) with ad slot test content
129+
- [x] Create [`fixtures/frameworks/nextjs/Dockerfile`](crates/integration-tests/fixtures/frameworks/nextjs/Dockerfile) (multi-stage build)
130+
- [x] Create [`tests/frameworks/nextjs.rs`](crates/integration-tests/tests/frameworks/nextjs.rs)
131+
- [x] Implement `NextJs` struct with RSC-specific scenarios
132+
- [x] Add Next.js to `FRAMEWORKS` registry
133+
- [x] Create `test_nextjs_fastly()` test
134+
- [x] Create `test_all_combinations()` matrix test
135+
- [ ] Verify: `cargo test -p integration-tests` passes for all tests (requires Docker)
136+
137+
### Phase 5: Documentation and CI ⏱️ 3-4 hours ✅ COMPLETE
138+
- [x] Create [`.github/workflows/integration-tests.yml`](.github/workflows/integration-tests.yml)
139+
- [x] Build WASM binary step
140+
- [x] Build Docker images (WordPress, Next.js)
141+
- [x] Install Viceroy (with caching)
142+
- [x] Run integration tests with WASM_BINARY_PATH env var
143+
- [x] Create [`crates/integration-tests/.dockerignore`](crates/integration-tests/.dockerignore)
144+
- [x] Update root [`Cargo.toml`](Cargo.toml) workspace members
145+
- [ ] Test CI on PR branch
150146
- [ ] Verify: CI passes on test branch
151147

152148
### Success Criteria
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
target/
2+
*.rs
3+
Cargo.toml
4+
Cargo.lock
5+
tests/
6+
README.md

crates/integration-tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ path = "tests/integration.rs"
1010
harness = true
1111

1212
[dev-dependencies]
13-
testcontainers = "0.25"
13+
testcontainers = { version = "0.25", features = ["blocking"] }
1414
reqwest = { version = "0.12", features = ["blocking"] }
1515
scraper = "0.21"
1616
tempfile = "3.0"
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Fastly Compute configuration template for integration tests.
2+
# The test harness mutates `publisher.origin_url` and integration toggles
3+
# at runtime via RuntimeConfigBuilder.
4+
5+
[[handlers]]
6+
path = "^/secure"
7+
username = "user"
8+
password = "pass"
9+
10+
[publisher]
11+
domain = "test-publisher.com"
12+
cookie_domain = ".test-publisher.com"
13+
origin_url = "PLACEHOLDER"
14+
proxy_secret = "integration-test-proxy-secret"
15+
16+
[synthetic]
17+
counter_store = "counter_store"
18+
opid_store = "opid_store"
19+
secret_key = "integration-test-secret"
20+
template = "{{ client_ip }}:{{ user_agent }}:{{ accept_language }}:{{ accept_encoding }}"
21+
22+
[request_signing]
23+
enabled = false
24+
config_store_id = "unused"
25+
secret_store_id = "unused"
26+
27+
[integrations.prebid]
28+
enabled = false
29+
server_url = "http://127.0.0.1:9999"
30+
timeout_ms = 1000
31+
bidders = ["appnexus"]
32+
debug = false
33+
34+
[integrations.nextjs]
35+
enabled = false
36+
rewrite_attributes = ["href", "link", "siteBaseUrl", "siteProductionDomain", "url"]
37+
max_combined_payload_bytes = 10485760
38+
39+
[integrations.testlight]
40+
endpoint = "https://testlight.example/openrtb2/auction"
41+
timeout_ms = 1200
42+
rewrite_scripts = true
43+
44+
[integrations.didomi]
45+
enabled = false
46+
sdk_origin = "https://sdk.privacy-center.org"
47+
api_origin = "https://api.privacy-center.org"
48+
49+
[integrations.permutive]
50+
enabled = false
51+
organization_id = ""
52+
workspace_id = ""
53+
project_id = ""
54+
api_endpoint = "https://api.permutive.com"
55+
secure_signals_endpoint = "https://secure-signals.permutive.app"
56+
57+
[integrations.lockr]
58+
enabled = false
59+
app_id = ""
60+
api_endpoint = "https://identity.loc.kr"
61+
sdk_url = "https://aim.loc.kr/identity-lockr-v1.0.js"
62+
cache_ttl_seconds = 3600
63+
rewrite_sdk = true
64+
65+
[integrations.datadome]
66+
enabled = false
67+
sdk_origin = "https://js.datadome.co"
68+
api_origin = "https://api-js.datadome.co"
69+
cache_ttl_seconds = 3600
70+
rewrite_sdk = true
71+
72+
[integrations.gpt]
73+
enabled = false
74+
script_url = "https://securepubads.g.doubleclick.net/tag/js/gpt.js"
75+
cache_ttl_seconds = 3600
76+
rewrite_script = true
77+
78+
[auction]
79+
enabled = false
80+
providers = ["prebid"]
81+
timeout_ms = 2000
82+
allowed_context_keys = []
83+
84+
[integrations.aps]
85+
enabled = false
86+
pub_id = "test-aps-publisher-id"
87+
endpoint = "https://origin-mocktioneer.cdintel.com/e/dtb/bid"
88+
timeout_ms = 1000
89+
90+
[integrations.google_tag_manager]
91+
enabled = false
92+
container_id = "GTM-TEST"
93+
94+
[integrations.adserver_mock]
95+
enabled = false
96+
endpoint = "https://origin-mocktioneer.cdintel.com/adserver/mediate"
97+
timeout_ms = 1000
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Viceroy local server configuration template for integration tests.
2+
# This configures the Viceroy runtime itself (backends, KV stores, etc.),
3+
# separate from the application config (trusted-server.toml).
4+
5+
[local_server]
6+
7+
[local_server.backends]
8+
9+
[local_server.kv_stores]
10+
[[local_server.kv_stores.counter_store]]
11+
key = "placeholder"
12+
data = "placeholder"
13+
14+
[[local_server.kv_stores.opid_store]]
15+
key = "placeholder"
16+
data = "placeholder"
17+
18+
[[local_server.kv_stores.creative_store]]
19+
key = "placeholder"
20+
data = "placeholder"
21+
22+
[local_server.secret_stores]
23+
[[local_server.secret_stores.signing_keys]]
24+
key = "ts-2025-10-A"
25+
data = "NVnTYrw5xoyTJDOwoUWoPJO3A6UCCXOJJUzgGTxxx7k="
26+
27+
[[local_server.secret_stores.api-keys]]
28+
key = "api_key"
29+
data = "test-api-key"
30+
31+
[local_server.config_stores]
32+
[local_server.config_stores.jwks_store]
33+
format = "inline-toml"
34+
[local_server.config_stores.jwks_store.contents]
35+
ts-2025-10-A = "{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"kid\":\"ts-2025-10-A\",\"use\":\"sig\",\"x\":\"UVTi04QLrIuB7jXpVfHjUTVN5aIdcbPNr50umTtN8pw\"}"
36+
ts-2025-10-B = "{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"kid\":\"ts-2025-10-B\",\"use\":\"sig\",\"x\":\"HVTi04QLrIuB7jXpVfHjUTVN5aIdcbPNr50umTtN8pw\"}"
37+
current-kid = "ts-2025-10-A"
38+
active-kids = "ts-2025-10-A,ts-2025-10-B"

0 commit comments

Comments
 (0)