Skip to content

Commit f13d3e3

Browse files
authored
Merge pull request #12 from ChorusOne/modifiers-tests
Add integration tests for request/response modifiers
2 parents 4b5f56e + 8bcbce5 commit f13d3e3

File tree

4 files changed

+196
-52
lines changed

4 files changed

+196
-52
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ vcr-cassette = "2"
4242
[dev-dependencies]
4343
tokio = { version = "1.17.0", features = ["full"] }
4444
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
45+
url = "2.4"
46+

tests/integration/e2e.rs

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,9 @@
11
use reqwest::Client;
2-
use std::{path::PathBuf, sync::Arc, time::Duration};
3-
use tokio::sync::Mutex;
2+
use std::{path::PathBuf, time::Duration};
43

54
use http::header::ACCEPT;
65
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
76
use rvcr::VCRMiddleware;
8-
use tracing_subscriber::{
9-
filter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
10-
};
11-
12-
lazy_static::lazy_static! {
13-
static ref SCOPE: TestScope = TestScope::default();
14-
static ref ADDRESS: String = String::from("http://127.0.0.1:38282");
15-
}
16-
17-
#[derive(Clone)]
18-
pub struct TestScope {
19-
pub initialized: Arc<Mutex<bool>>,
20-
}
21-
22-
impl Default for TestScope {
23-
fn default() -> Self {
24-
Self {
25-
initialized: Arc::new(Mutex::new(false)),
26-
}
27-
}
28-
}
29-
30-
impl TestScope {
31-
pub async fn init(self) {
32-
let mut inited = self.initialized.lock().await;
33-
if *inited == false {
34-
if std::env::var("TEST_LOG").is_ok() {
35-
let stdout_log = tracing_subscriber::fmt::layer().pretty();
36-
tracing_subscriber::registry()
37-
.with(
38-
stdout_log
39-
// Add an `INFO` filter to the stdout logging layer
40-
.with_filter(filter::LevelFilter::DEBUG),
41-
)
42-
.init();
43-
}
44-
*inited = true;
45-
}
46-
}
47-
}
487

498
async fn send_and_compare(
509
method: reqwest::Method,
@@ -54,9 +13,12 @@ async fn send_and_compare(
5413
vcr_client: ClientWithMiddleware,
5514
real_client: reqwest::Client,
5615
) {
57-
let mut req1 = vcr_client.request(method.clone(), format!("{}{}", ADDRESS.to_string(), path));
16+
let mut req1 = vcr_client.request(
17+
method.clone(),
18+
format!("{}{}", crate::ADDRESS.to_string(), path),
19+
);
5820

59-
let mut req2 = real_client.request(method, format!("{}{}", ADDRESS.to_string(), path));
21+
let mut req2 = real_client.request(method, format!("{}{}", crate::ADDRESS.to_string(), path));
6022

6123
for (header_name, header_value) in headers {
6224
req1 = req1.header(header_name.clone(), header_value);
@@ -97,7 +59,7 @@ async fn send_and_compare(
9759

9860
#[tokio::test]
9961
async fn test_rvcr_replay() {
100-
SCOPE.clone().init().await;
62+
crate::SCOPE.clone().init().await;
10163
let mut bundle = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
10264
bundle.push("tests/resources/replay.vcr.json");
10365

@@ -142,7 +104,7 @@ async fn test_rvcr_replay() {
142104

143105
#[tokio::test]
144106
async fn test_rvcr_replay_search_all() {
145-
SCOPE.clone().init().await;
107+
crate::SCOPE.clone().init().await;
146108
let mut bundle = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
147109
bundle.push("tests/resources/search-all.vcr.json");
148110

@@ -188,7 +150,7 @@ async fn test_rvcr_replay_search_all() {
188150
let req1 = vcr_client
189151
.request(
190152
reqwest::Method::POST,
191-
format!("{}{}", ADDRESS.to_string(), "/post"),
153+
format!("{}{}", crate::ADDRESS.to_string(), "/post"),
192154
)
193155
.send()
194156
.await
@@ -201,7 +163,7 @@ async fn test_rvcr_replay_search_all() {
201163
let req2 = vcr_client
202164
.request(
203165
reqwest::Method::POST,
204-
format!("{}{}", ADDRESS.to_string(), "/post"),
166+
format!("{}{}", crate::ADDRESS.to_string(), "/post"),
205167
)
206168
.send()
207169
.await
@@ -217,7 +179,7 @@ async fn test_rvcr_replay_search_all() {
217179

218180
#[tokio::test]
219181
async fn test_rvcr_replay_skip_found() {
220-
SCOPE.clone().init().await;
182+
crate::SCOPE.clone().init().await;
221183
let mut bundle = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
222184
bundle.push("tests/resources/skip-found.vcr.json");
223185

@@ -263,7 +225,7 @@ async fn test_rvcr_replay_skip_found() {
263225
let req1 = vcr_client
264226
.request(
265227
reqwest::Method::POST,
266-
format!("{}{}", ADDRESS.to_string(), "/post"),
228+
format!("{}{}", crate::ADDRESS.to_string(), "/post"),
267229
)
268230
.send()
269231
.await
@@ -274,7 +236,7 @@ async fn test_rvcr_replay_skip_found() {
274236
let req2 = vcr_client
275237
.request(
276238
reqwest::Method::POST,
277-
format!("{}{}", ADDRESS.to_string(), "/post"),
239+
format!("{}{}", crate::ADDRESS.to_string(), "/post"),
278240
)
279241
.send()
280242
.await
@@ -291,7 +253,7 @@ async fn test_rvcr_replay_skip_found() {
291253
#[cfg(feature = "compress")]
292254
#[tokio::test]
293255
async fn test_rvcr_replay_compressed() {
294-
SCOPE.clone().init().await;
256+
crate::SCOPE.clone().init().await;
295257
let mut bundle = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
296258
bundle.push("tests/resources/replay.vcr.zip");
297259

tests/integration/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,46 @@
1+
use std::sync::Arc;
2+
3+
use tokio::sync::Mutex;
4+
use tracing_subscriber::{
5+
filter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
6+
};
7+
18
mod e2e;
9+
mod modifiers;
10+
11+
#[derive(Clone)]
12+
pub struct TestScope {
13+
pub initialized: Arc<Mutex<bool>>,
14+
}
15+
16+
impl Default for TestScope {
17+
fn default() -> Self {
18+
Self {
19+
initialized: Arc::new(Mutex::new(false)),
20+
}
21+
}
22+
}
23+
24+
impl TestScope {
25+
pub async fn init(self) {
26+
let mut inited = self.initialized.lock().await;
27+
if *inited == false {
28+
if std::env::var("TEST_LOG").is_ok() {
29+
let stdout_log = tracing_subscriber::fmt::layer().pretty();
30+
tracing_subscriber::registry()
31+
.with(
32+
stdout_log
33+
// Add an `INFO` filter to the stdout logging layer
34+
.with_filter(filter::LevelFilter::DEBUG),
35+
)
36+
.init();
37+
}
38+
*inited = true;
39+
}
40+
}
41+
}
42+
43+
lazy_static::lazy_static! {
44+
static ref SCOPE: TestScope = TestScope::default();
45+
static ref ADDRESS: String = String::from("http://127.0.0.1:38282");
46+
}

tests/integration/modifiers.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use std::borrow::Cow;
2+
use std::fs;
3+
use std::path::PathBuf;
4+
5+
use reqwest_middleware::ClientBuilder;
6+
use reqwest_middleware::ClientWithMiddleware;
7+
use rvcr::VCRMiddleware;
8+
use rvcr::VCRMode;
9+
use vcr_cassette::{Request, Response};
10+
11+
// Replace access_token and secret header values with dummy ones
12+
fn filter_query_params(mut uri: url::Url) -> url::Url {
13+
let sensitive_query_params = ["access_token", "secret"];
14+
let cloned = uri.clone();
15+
let filtered_query_params = cloned.query_pairs().map(|(k, v)| {
16+
if sensitive_query_params.contains(&k.as_ref()) {
17+
(k.clone(), Cow::from(format!("__{}__", k.to_uppercase())))
18+
} else {
19+
(k, v)
20+
}
21+
});
22+
uri.query_pairs_mut()
23+
.clear()
24+
.extend_pairs(filtered_query_params)
25+
.finish();
26+
uri
27+
}
28+
29+
fn request_modifier(req: &mut Request) {
30+
// Overwrite query params with filtered ones
31+
req.uri = filter_query_params(req.uri.clone());
32+
}
33+
34+
fn response_modifier(resp: &mut Response) {
35+
for (name, value) in &mut resp.headers {
36+
if name == "server" {
37+
(*value).pop().unwrap();
38+
(*value).push("Test Server Header Emulated Expect".to_string());
39+
}
40+
}
41+
}
42+
43+
fn saved_fixture_path(path: &str) -> PathBuf {
44+
let mut bundle = PathBuf::from(std::env::temp_dir());
45+
bundle.push(path);
46+
47+
if bundle.exists() {
48+
std::fs::remove_file(bundle.clone()).unwrap();
49+
}
50+
bundle
51+
}
52+
53+
#[tokio::test]
54+
pub async fn test_modifier_request() {
55+
crate::SCOPE.clone().init().await;
56+
let bundle = saved_fixture_path("request-modifer-test-case.vcr.json");
57+
58+
let middleware = VCRMiddleware::try_from(bundle.clone())
59+
.unwrap()
60+
.with_mode(VCRMode::Record)
61+
.with_modify_request(request_modifier);
62+
63+
let vcr_client: ClientWithMiddleware = ClientBuilder::new(reqwest::Client::new())
64+
.with(middleware)
65+
.build();
66+
67+
vcr_client
68+
.request(
69+
reqwest::Method::POST,
70+
format!(
71+
"{}{}",
72+
crate::ADDRESS.to_string(),
73+
"/post?access_token=s3cr3t&spam=eggs&secret=s3cr3t",
74+
),
75+
)
76+
.send()
77+
.await
78+
.expect("Can not send request");
79+
// Drop triggers recording
80+
drop(vcr_client);
81+
82+
let vcr_content = fs::read(bundle.clone()).expect("VCR file not created during test case");
83+
84+
let cassette: vcr_cassette::Cassette = serde_json::from_slice(&vcr_content).unwrap();
85+
let interaction = cassette.http_interactions.first().unwrap();
86+
let recorded_url = interaction.request.uri.to_string();
87+
// Secret parameters were replaced
88+
assert_eq!(
89+
format!(
90+
"{}/post?access_token=__ACCESS_TOKEN__&spam=eggs&secret=__SECRET__",
91+
crate::ADDRESS.to_string()
92+
),
93+
recorded_url,
94+
)
95+
}
96+
97+
#[tokio::test]
98+
pub async fn test_modifier_response() {
99+
crate::SCOPE.clone().init().await;
100+
let bundle = saved_fixture_path("response-modifer-test-case.vcr.json");
101+
let middleware = VCRMiddleware::try_from(bundle.clone())
102+
.unwrap()
103+
.with_mode(VCRMode::Record)
104+
.with_modify_response(response_modifier);
105+
106+
let vcr_client: ClientWithMiddleware = ClientBuilder::new(reqwest::Client::new())
107+
.with(middleware)
108+
.build();
109+
110+
vcr_client
111+
.request(
112+
reqwest::Method::POST,
113+
format!("{}{}", crate::ADDRESS.to_string(), "/post",),
114+
)
115+
.send()
116+
.await
117+
.expect("Can not send request");
118+
// Drop triggers recording
119+
drop(vcr_client);
120+
121+
let vcr_content = fs::read(bundle.clone()).expect("VCR file not created during test case");
122+
123+
let cassette: vcr_cassette::Cassette = serde_json::from_slice(&vcr_content).unwrap();
124+
let interaction = cassette.http_interactions.first().unwrap();
125+
let recorded_server_header = interaction
126+
.response
127+
.headers
128+
.get("server")
129+
.unwrap()
130+
.clone()
131+
.pop()
132+
.unwrap();
133+
// Server header was replaced
134+
assert_eq!(recorded_server_header, "Test Server Header Emulated Expect",)
135+
}

0 commit comments

Comments
 (0)