151 lines
5.0 KiB
Rust
151 lines
5.0 KiB
Rust
mod scope_tests {
|
|
use super::*;
|
|
use http_body_util::BodyExt;
|
|
async fn fill(cache: &Arc<Cache>, ticket: Ticket) {
|
|
let settings = Settings {
|
|
enabled: true,
|
|
..Settings::default()
|
|
};
|
|
let mut response = Response::new(Body::from("scope-payload"));
|
|
response.headers_mut().insert(
|
|
"cache-control",
|
|
HeaderValue::from_static("public, max-age=30"),
|
|
);
|
|
let plan = plan(
|
|
StatusCode::OK,
|
|
response.headers(),
|
|
&settings,
|
|
Duration::ZERO,
|
|
)
|
|
.unwrap();
|
|
cache
|
|
.wrap(response, ticket, plan, &settings)
|
|
.into_body()
|
|
.collect()
|
|
.await
|
|
.unwrap();
|
|
}
|
|
#[tokio::test]
|
|
async fn selective_purge_preserves_other_hosts_and_cancels_old_fills() {
|
|
let cache = Cache::new(1);
|
|
let ticket = |scope| {
|
|
cache
|
|
.ticket_scoped(
|
|
1,
|
|
scope,
|
|
"http",
|
|
"same.test",
|
|
"root",
|
|
"/",
|
|
&HeaderMap::new(),
|
|
)
|
|
.unwrap()
|
|
};
|
|
let a = ticket("a");
|
|
let b = ticket("b");
|
|
fill(&cache, a.clone()).await;
|
|
fill(&cache, b.clone()).await;
|
|
assert_eq!(cache.scope_view("a").entries, 1);
|
|
assert_eq!(cache.purge_scope("a"), 1);
|
|
assert!(cache.get(&a).is_none());
|
|
assert!(cache.get(&b).is_some());
|
|
fill(&cache, a).await;
|
|
assert_eq!(cache.scope_view("a").entries, 0);
|
|
fill(&cache, ticket("a")).await;
|
|
assert_eq!(cache.scope_view("a").entries, 1);
|
|
}
|
|
#[test]
|
|
fn stale_snapshot_cannot_issue_cache_ticket_after_config_commit() {
|
|
let cache = Cache::new(1);
|
|
cache.set_control_revision(7);
|
|
let ticket = |revision| cache.ticket_scoped_for_config(
|
|
revision, 1, "vhost-a", "http", "same.test", "main", "/resource", &HeaderMap::new()
|
|
);
|
|
assert!(ticket(7).is_some(), "the current snapshot can cache");
|
|
cache.set_control_revision(8);
|
|
assert_eq!(cache.revision(), 1);
|
|
assert!(ticket(7).is_none(), "late request from the previous snapshot must bypass");
|
|
assert!(ticket(8).is_some(), "the new snapshot can reuse the same cache identity");
|
|
}
|
|
|
|
#[test]
|
|
fn per_vhost_settings_inherit_and_cannot_expand_shared_capacity() {
|
|
let global = Settings::default();
|
|
let effective = Overrides {
|
|
enabled: Some(true),
|
|
max_ttl_seconds: Some(30),
|
|
expose_status: Some(true),
|
|
..Overrides::default()
|
|
}
|
|
.resolve(&global)
|
|
.unwrap();
|
|
assert!(effective.enabled && effective.expose_status);
|
|
let mut globally_disabled = global.clone();
|
|
globally_disabled.enabled = false;
|
|
assert!(!Overrides { enabled: Some(true), ..Overrides::default() }
|
|
.resolve(&globally_disabled)
|
|
.unwrap()
|
|
.enabled);
|
|
assert!(!Overrides { enabled: Some(false), ..Overrides::default() }
|
|
.resolve(&global)
|
|
.unwrap()
|
|
.enabled);
|
|
assert!(!Overrides::default()
|
|
.resolve(&globally_disabled)
|
|
.unwrap()
|
|
.enabled);
|
|
assert_eq!(effective.max_bytes, global.max_bytes);
|
|
assert_eq!(effective.max_ttl_seconds, 30);
|
|
assert!(Overrides {
|
|
max_object_bytes: Some(global.max_object_bytes + 1),
|
|
..Overrides::default()
|
|
}
|
|
.resolve(&global)
|
|
.is_err());
|
|
assert!(Overrides {
|
|
max_ttl_seconds: Some(1),
|
|
default_ttl_seconds: Some(2),
|
|
..Overrides::default()
|
|
}
|
|
.resolve(&global)
|
|
.is_err());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unsafe_force_cache_is_per_vhost_and_matches_path_or_target_prefix() {
|
|
let force = UnsafeForce {
|
|
enabled: true,
|
|
targets: vec!["/convert?url=".into(), "/assets".into()],
|
|
};
|
|
force.validate().unwrap();
|
|
assert!(force.matches(
|
|
"/convert",
|
|
"/convert?url=https%3A%2F%2Fexample.test%2Flist.txt"
|
|
));
|
|
assert!(force.matches("/assets/app.js", "/assets/app.js?v=1"));
|
|
assert!(!force.matches("/api", "/api?url=x"));
|
|
assert!(UnsafeForce {
|
|
enabled: true,
|
|
targets: vec!["https://example.test/".into()],
|
|
}
|
|
.validate()
|
|
.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn forced_request_policy_keeps_protocol_safety_but_ignores_cookie_and_client_no_store() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("cookie", HeaderValue::from_static("session=abc"));
|
|
headers.insert("authorization", HeaderValue::from_static("Bearer token"));
|
|
headers.insert("cache-control", HeaderValue::from_static("no-store"));
|
|
assert_eq!(
|
|
forced_request_policy(&Method::GET, &headers, true),
|
|
RequestPolicy::Use
|
|
);
|
|
assert_eq!(
|
|
forced_request_policy(&Method::POST, &headers, true),
|
|
RequestPolicy::Bypass("request_method")
|
|
);
|
|
}
|