Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e749061

Browse files
committedSep 27, 2024·
add tests; review error handling
1 parent 52e0244 commit e749061

File tree

7 files changed

+389
-136
lines changed

7 files changed

+389
-136
lines changed
 

‎cpp-linter/src/clang_tools/clang_format.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,13 @@ pub fn run_clang_format(
160160
.output()
161161
.with_context(|| format!("Failed to get replacements from clang-format: {file_name}"))?;
162162
if !output.stderr.is_empty() || !output.status.success() {
163-
if let Ok(stderr) = String::from_utf8(output.stderr) {
164-
logs.push((
165-
log::Level::Debug,
166-
format!("clang-format raised the follow errors:\n{}", stderr),
167-
));
168-
} else {
169-
logs.push((
170-
log::Level::Error,
171-
"stderr from clang-format was not UTF-8 encoded".to_string(),
172-
));
173-
}
163+
logs.push((
164+
log::Level::Debug,
165+
format!(
166+
"clang-format raised the follow errors:\n{}",
167+
String::from_utf8_lossy(&output.stderr)
168+
),
169+
));
174170
}
175171
if output.stdout.is_empty() {
176172
return Ok(logs);

‎cpp-linter/src/clang_tools/clang_tidy.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -304,21 +304,17 @@ pub fn run_clang_tidy(
304304
log::Level::Debug,
305305
format!(
306306
"Output from clang-tidy:\n{}",
307-
String::from_utf8(output.stdout.to_vec()).unwrap()
307+
String::from_utf8_lossy(&output.stdout)
308308
),
309309
));
310310
if !output.stderr.is_empty() {
311-
if let Ok(stderr) = String::from_utf8(output.stderr) {
312-
logs.push((
313-
log::Level::Debug,
314-
format!("clang-tidy made the following summary:\n{}", stderr),
315-
));
316-
} else {
317-
logs.push((
318-
log::Level::Error,
319-
"clang-tidy stderr is not UTF-8 encoded".to_string(),
320-
));
321-
}
311+
logs.push((
312+
log::Level::Debug,
313+
format!(
314+
"clang-tidy made the following summary:\n{}",
315+
String::from_utf8_lossy(&output.stderr)
316+
),
317+
));
322318
}
323319
file.tidy_advice = parse_tidy_output(&output.stdout, &clang_params.database_json)?;
324320
if clang_params.tidy_review {

‎cpp-linter/src/rest_api/github/mod.rs

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -292,31 +292,31 @@ mod test {
292292
default::Default,
293293
env,
294294
io::Read,
295-
path::PathBuf,
295+
path::{Path, PathBuf},
296296
sync::{Arc, Mutex},
297297
};
298298

299-
use chrono::Utc;
300-
use mockito::{Matcher, Server};
301299
use regex::Regex;
302-
use reqwest::{Method, Url};
303300
use tempfile::{tempdir, NamedTempFile};
304301

305302
use super::GithubApiClient;
306303
use crate::{
307304
clang_tools::capture_clang_tools_output,
308305
cli::{ClangParams, FeedbackInput, LinesChangedOnly},
309-
common_fs::FileObj,
306+
common_fs::{FileFilter, FileObj},
307+
logger,
310308
rest_api::{RestApiClient, USER_OUTREACH},
311309
};
312310

313311
// ************************* tests for step-summary and output variables
314312

315-
async fn create_comment(tidy_checks: &str, style: &str) -> (String, String) {
313+
async fn create_comment(tidy_checks: &str, style: &str, fail_gh_out: bool) -> (String, String) {
316314
let tmp_dir = tempdir().unwrap();
317315
let rest_api_client = GithubApiClient::new().unwrap();
316+
logger::init().unwrap();
318317
if env::var("ACTIONS_STEP_DEBUG").is_ok_and(|var| var == "true") {
319318
assert!(rest_api_client.debug_enabled);
319+
log::set_max_level(log::LevelFilter::Debug);
320320
}
321321
let mut files = vec![Arc::new(Mutex::new(FileObj::new(PathBuf::from(
322322
"tests/demo/demo.cpp",
@@ -342,7 +342,14 @@ mod test {
342342
let mut step_summary_path = NamedTempFile::new_in(tmp_dir.path()).unwrap();
343343
env::set_var("GITHUB_STEP_SUMMARY", step_summary_path.path());
344344
let mut gh_out_path = NamedTempFile::new_in(tmp_dir.path()).unwrap();
345-
env::set_var("GITHUB_OUTPUT", gh_out_path.path());
345+
env::set_var(
346+
"GITHUB_OUTPUT",
347+
if fail_gh_out {
348+
Path::new("not-a-file.txt")
349+
} else {
350+
gh_out_path.path()
351+
},
352+
);
346353
rest_api_client
347354
.post_feedback(&files, feedback_inputs)
348355
.await
@@ -354,13 +361,15 @@ mod test {
354361
assert!(&step_summary_content.contains(USER_OUTREACH));
355362
let mut gh_out_content = String::new();
356363
gh_out_path.read_to_string(&mut gh_out_content).unwrap();
357-
assert!(gh_out_content.starts_with("checks-failed="));
364+
if !fail_gh_out {
365+
assert!(gh_out_content.starts_with("checks-failed="));
366+
}
358367
(step_summary_content, gh_out_content)
359368
}
360369

361370
#[tokio::test]
362371
async fn check_comment_concerns() {
363-
let (comment, gh_out) = create_comment("readability-*", "file").await;
372+
let (comment, gh_out) = create_comment("readability-*", "file", false).await;
364373
assert!(&comment.contains(":warning:\nSome files did not pass the configured checks!\n"));
365374
let fmt_pattern = Regex::new(r"format-checks-failed=(\d+)\n").unwrap();
366375
let tidy_pattern = Regex::new(r"tidy-checks-failed=(\d+)\n").unwrap();
@@ -380,61 +389,31 @@ mod test {
380389
#[tokio::test]
381390
async fn check_comment_lgtm() {
382391
env::set_var("ACTIONS_STEP_DEBUG", "true");
383-
let (comment, gh_out) = create_comment("-*", "").await;
392+
let (comment, gh_out) = create_comment("-*", "", false).await;
384393
assert!(&comment.contains(":heavy_check_mark:\nNo problems need attention."));
385394
assert_eq!(
386395
&gh_out,
387396
"checks-failed=0\nformat-checks-failed=0\ntidy-checks-failed=0\n"
388397
);
389398
}
390399

391-
async fn simulate_rate_limit(secondary: bool) {
392-
let mut server = Server::new_async().await;
393-
let url = Url::parse(server.url().as_str()).unwrap();
394-
env::set_var("GITHUB_API_URL", server.url());
395-
let client = GithubApiClient::new().unwrap();
396-
let reset_timestamp = (Utc::now().timestamp() + 60).to_string();
397-
let mock = server
398-
.mock("GET", "/")
399-
.match_body(Matcher::Any)
400-
.expect_at_least(1)
401-
.expect_at_most(5)
402-
.with_status(429)
403-
.with_header(
404-
&client.rate_limit_headers.remaining,
405-
if secondary { "1" } else { "0" },
406-
)
407-
.with_header(&client.rate_limit_headers.reset, &reset_timestamp);
408-
if secondary {
409-
mock.with_header(&client.rate_limit_headers.retry, "0")
410-
.create();
411-
} else {
412-
mock.create();
413-
}
414-
let request =
415-
GithubApiClient::make_api_request(&client.client, url, Method::GET, None, None)
416-
.unwrap();
417-
GithubApiClient::send_api_request(
418-
client.client.clone(),
419-
request,
420-
client.rate_limit_headers.clone(),
421-
0,
422-
)
423-
.await
424-
.unwrap();
425-
}
426-
427400
#[tokio::test]
428-
#[ignore]
429-
#[should_panic(expected = "REST API secondary rate limit exceeded")]
430-
async fn secondary_rate_limit() {
431-
simulate_rate_limit(true).await;
401+
async fn fail_gh_output() {
402+
env::set_var("ACTIONS_STEP_DEBUG", "true");
403+
let (comment, gh_out) = create_comment("-*", "", true).await;
404+
assert!(&comment.contains(":heavy_check_mark:\nNo problems need attention."));
405+
assert_eq!(&gh_out, "");
432406
}
433407

434408
#[tokio::test]
435-
#[ignore]
436-
#[should_panic(expected = "REST API rate limit exceeded!")]
437-
async fn primary_rate_limit() {
438-
simulate_rate_limit(false).await;
409+
async fn fail_get_local_diff() {
410+
env::set_var("CI", "false");
411+
let tmp_dir = tempdir().unwrap();
412+
env::set_current_dir(tmp_dir.path()).unwrap();
413+
let rest_client = GithubApiClient::new().unwrap();
414+
let files = rest_client
415+
.get_list_of_changed_files(&FileFilter::new(&[], vec![]))
416+
.await;
417+
assert!(files.is_err())
439418
}
440419
}

‎cpp-linter/src/rest_api/github/specific_api.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ impl GithubApiClient {
3030
let pull_request = {
3131
match event_name.as_str() {
3232
"pull_request" => {
33-
let event_payload_path = env::var("GITHUB_EVENT_PATH").with_context(|| {
34-
"GITHUB_EVENT_NAME is set to 'pull_request', but GITHUB_EVENT_PATH is not set"
35-
})?;
33+
// GITHUB_*** env vars cannot be overwritten in CI runners on GitHub.
34+
let event_payload_path = env::var("GITHUB_EVENT_PATH")?;
35+
// event payload JSON file can be overwritten/removed in CI runners
3636
let file_buf = &mut String::new();
3737
OpenOptions::new()
3838
.read(true)
@@ -51,16 +51,15 @@ impl GithubApiClient {
5151
_ => None,
5252
}
5353
};
54+
// GITHUB_*** env vars cannot be overwritten in CI runners on GitHub.
5455
let gh_api_url = env::var("GITHUB_API_URL").unwrap_or("https://api.github.com".to_string());
55-
let api_url = Url::parse(gh_api_url.clone().as_str())
56-
.with_context(|| format!("Failed to parse URL from GITHUB_API_URL: {}", gh_api_url))?;
56+
let api_url = Url::parse(gh_api_url.as_str())?;
5757

5858
Ok(GithubApiClient {
5959
client: Client::builder()
6060
.default_headers(Self::make_headers()?)
6161
.user_agent(USER_AGENT)
62-
.build()
63-
.with_context(|| "Failed to create a session client for REST API calls")?,
62+
.build()?,
6463
pull_request,
6564
event_name,
6665
api_url,
@@ -87,6 +86,7 @@ impl GithubApiClient {
8786
/// Append step summary to CI workflow's summary page.
8887
pub fn post_step_summary(&self, comment: &String) {
8988
if let Ok(gh_out) = env::var("GITHUB_STEP_SUMMARY") {
89+
// step summary MD file can be overwritten/removed in CI runners
9090
if let Ok(mut gh_out_file) = OpenOptions::new().append(true).open(gh_out) {
9191
if let Err(e) = writeln!(gh_out_file, "\n{}\n", comment) {
9292
log::error!("Could not write to GITHUB_STEP_SUMMARY file: {}", e);

‎cpp-linter/src/rest_api/mod.rs

Lines changed: 265 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@
129129
"Failed to parse i64 from remaining attempts about rate limit: {count}"
130130
);
131131
}
132-
} else {
133-
log::debug!("Failed to extract remaining attempts about rate limit: {remaining:?}");
134132
}
135133
} else {
134+
// NOTE: I guess it is sometimes valid for a request to
135+
// not include remaining rate limit attempts
136136
log::debug!("Response headers do not include remaining API usage count");
137137
}
138138
if requests_remaining.is_some_and(|v| v <= 0) {
@@ -145,20 +145,17 @@
145145
"REST API rate limit exceeded! Resets at {}",
146146
reset
147147
));
148-
} else {
149-
log::debug!("Rate limit reset UTC timestamp is an invalid: {value}");
150148
}
151149
} else {
152150
log::debug!(
153151
"Failed to parse i64 from reset time about rate limit: {epoch}"
154152
);
155153
}
156-
} else {
157-
log::debug!("Failed to extract reset info about rate limit: {reset_value:?}");
158154
}
159155
} else {
160156
log::debug!("Response headers does not include a reset timestamp");
161157
}
158+
return Err(anyhow!("REST API rate limit exceeded!"));
162159
}
163160

164161
// check if secondary rate limit is violated; backoff and try again.
@@ -175,9 +172,7 @@
175172
"Failed to parse u64 from retry interval about rate limit: {retry_str}"
176173
);
177174
}
178-
} else {
179-
log::debug!("Failed to extract retry interval about rate limit: {retry_value:?}");
180175
}
181176
return Self::send_api_request(
182177
client,
183178
request,
@@ -296,8 +291,6 @@
296291
}
297292
}
298293
}
299-
} else {
300-
log::debug!("Failed to convert header value of links to a str");
301294
}
302295
}
303296
None
@@ -309,7 +302,7 @@
309302
log::error!("{}: {e:?}", context.to_owned());
310303
if let Ok(text) = response.text().await {
311304
log::error!("{text}");
312305
}
313306
}
314307
}
315308
}
@@ -390,3 +383,265 @@
390383
comment.push_str(&tidy_comment);
391384
comment.push_str(&closer);
392385
}
386+
387+
/// This module tests the silent errors' debug logs
388+
/// from `try_next_page()` and `send_api_request()` functions.
389+
#[cfg(test)]
390+
mod test {
391+
use std::sync::{Arc, Mutex};
392+
393+
use anyhow::{anyhow, Result};
394+
use chrono::Utc;
395+
use mockito::{Matcher, Server};
396+
use reqwest::{
397+
header::{HeaderMap, HeaderValue},
398+
Client,
399+
};
400+
use reqwest::{Method, Url};
401+
402+
use crate::{
403+
cli::FeedbackInput,
404+
common_fs::{FileFilter, FileObj},
405+
logger,
406+
};
407+
408+
use super::{RestApiClient, RestApiRateLimitHeaders};
409+
410+
/// A dummy struct to impl RestApiClient
411+
#[derive(Default)]
412+
struct TestClient {}
413+
414+
impl RestApiClient for TestClient {
415+
fn set_exit_code(
416+
&self,
417+
_checks_failed: u64,
418+
_format_checks_failed: Option<u64>,
419+
_tidy_checks_failed: Option<u64>,
420+
) -> u64 {
421+
0
422+
}
423+
424+
fn make_headers() -> Result<HeaderMap<HeaderValue>> {
425+
Err(anyhow!("Not implemented"))
426+
}
427+
428+
async fn get_list_of_changed_files(
429+
&self,
430+
_file_filter: &FileFilter,
431+
) -> Result<Vec<FileObj>> {
432+
Err(anyhow!("Not implemented"))
433+
}
434+
435+
async fn get_changed_files_paginated(
436+
&self,
437+
_url: reqwest::Url,
438+
_file_filter: &FileFilter,
439+
) -> Result<Vec<FileObj>> {
440+
Err(anyhow!("Not implemented"))
441+
}
442+
443+
async fn post_feedback(
444+
&self,
445+
_files: &[Arc<Mutex<FileObj>>],
446+
_user_inputs: FeedbackInput,
447+
) -> Result<u64> {
448+
Err(anyhow!("Not implemented"))
449+
}
450+
}
451+
452+
#[tokio::test]
453+
async fn dummy_coverage() {
454+
assert!(TestClient::make_headers().is_err());
455+
let dummy = TestClient::default();
456+
assert_eq!(dummy.set_exit_code(1, None, None), 0);
457+
assert!(dummy
458+
.get_list_of_changed_files(&FileFilter::new(&[], vec![]))
459+
.await
460+
.is_err());
461+
assert!(dummy
462+
.get_changed_files_paginated(
463+
Url::parse("https://example.net").unwrap(),
464+
&FileFilter::new(&[], vec![])
465+
)
466+
.await
467+
.is_err());
468+
assert!(dummy
469+
.post_feedback(&[], FeedbackInput::default())
470+
.await
471+
.is_err())
472+
}
473+
474+
// ************************************************* try_next_page() tests
475+
476+
#[test]
477+
fn bad_link_header() {
478+
let mut headers = HeaderMap::with_capacity(1);
479+
assert!(headers
480+
.insert("link", HeaderValue::from_str("; rel=\"next\"").unwrap())
481+
.is_none());
482+
logger::init().unwrap();
483+
log::set_max_level(log::LevelFilter::Debug);
484+
let result = TestClient::try_next_page(&headers);
485+
assert!(result.is_none());
486+
}
487+
488+
#[test]
489+
fn bad_link_domain() {
490+
let mut headers = HeaderMap::with_capacity(1);
491+
assert!(headers
492+
.insert(
493+
"link",
494+
HeaderValue::from_str("<not a domain>; rel=\"next\"").unwrap()
495+
)
496+
.is_none());
497+
logger::init().unwrap();
498+
log::set_max_level(log::LevelFilter::Debug);
499+
let result = TestClient::try_next_page(&headers);
500+
assert!(result.is_none());
501+
}
502+
503+
// ************************************************* Rate Limit Tests
504+
505+
#[derive(Default)]
506+
struct RateLimitTestParams {
507+
secondary: bool,
508+
has_remaining_count: bool,
509+
bad_remaining_count: bool,
510+
has_reset_timestamp: bool,
511+
bad_reset_timestamp: bool,
512+
has_retry_interval: bool,
513+
bad_retry_interval: bool,
514+
}
515+
516+
async fn simulate_rate_limit(test_params: &RateLimitTestParams) {
517+
let rate_limit_headers = RestApiRateLimitHeaders {
518+
reset: "reset".to_string(),
519+
remaining: "remaining".to_string(),
520+
retry: "retry".to_string(),
521+
};
522+
logger::init().unwrap();
523+
log::set_max_level(log::LevelFilter::Debug);
524+
525+
let mut server = Server::new_async().await;
526+
let client = Client::new();
527+
let reset_timestamp = (Utc::now().timestamp() + 60).to_string();
528+
let mut mock = server
529+
.mock("GET", "/")
530+
.match_body(Matcher::Any)
531+
.expect_at_least(1)
532+
.expect_at_most(5)
533+
.with_status(429);
534+
if test_params.has_remaining_count {
535+
mock = mock.with_header(
536+
&rate_limit_headers.remaining,
537+
if test_params.secondary {
538+
"1"
539+
} else if test_params.bad_remaining_count {
540+
"X"
541+
} else {
542+
"0"
543+
},
544+
);
545+
}
546+
if test_params.has_reset_timestamp {
547+
mock = mock.with_header(
548+
&rate_limit_headers.reset,
549+
if test_params.bad_reset_timestamp {
550+
"X"
551+
} else {
552+
&reset_timestamp
553+
},
554+
);
555+
}
556+
if test_params.secondary && test_params.has_retry_interval {
557+
mock.with_header(
558+
&rate_limit_headers.retry,
559+
if test_params.bad_retry_interval {
560+
"X"
561+
} else {
562+
"0"
563+
},
564+
)
565+
.create();
566+
} else {
567+
mock.create();
568+
}
569+
let request =
570+
TestClient::make_api_request(&client, server.url(), Method::GET, None, None).unwrap();
571+
TestClient::send_api_request(client.clone(), request, rate_limit_headers.clone(), 0)
572+
.await
573+
.unwrap();
574+
}
575+
576+
#[tokio::test]
577+
#[ignore]
578+
#[should_panic(expected = "REST API secondary rate limit exceeded")]
579+
async fn rate_limit_secondary() {
580+
simulate_rate_limit(&RateLimitTestParams {
581+
secondary: true,
582+
has_retry_interval: true,
583+
..Default::default()
584+
})
585+
.await;
586+
}
587+
588+
#[tokio::test]
589+
#[ignore]
590+
#[should_panic(expected = "REST API secondary rate limit exceeded")]
591+
async fn rate_limit_bad_retry() {
592+
simulate_rate_limit(&RateLimitTestParams {
593+
secondary: true,
594+
has_retry_interval: true,
595+
bad_retry_interval: true,
596+
..Default::default()
597+
})
598+
.await;
599+
}
600+
601+
#[tokio::test]
602+
#[ignore]
603+
#[should_panic(expected = "REST API rate limit exceeded!")]
604+
async fn rate_limit_primary() {
605+
simulate_rate_limit(&RateLimitTestParams {
606+
has_remaining_count: true,
607+
has_reset_timestamp: true,
608+
..Default::default()
609+
})
610+
.await;
611+
}
612+
613+
#[tokio::test]
614+
#[ignore]
615+
#[should_panic(expected = "REST API rate limit exceeded!")]
616+
async fn rate_limit_no_reset() {
617+
simulate_rate_limit(&RateLimitTestParams {
618+
has_remaining_count: true,
619+
..Default::default()
620+
})
621+
.await;
622+
}
623+
624+
#[tokio::test]
625+
#[ignore]
626+
#[should_panic(expected = "REST API rate limit exceeded!")]
627+
async fn rate_limit_bad_reset() {
628+
simulate_rate_limit(&RateLimitTestParams {
629+
has_remaining_count: true,
630+
has_reset_timestamp: true,
631+
bad_reset_timestamp: true,
632+
..Default::default()
633+
})
634+
.await;
635+
}
636+
637+
#[tokio::test]
638+
#[ignore]
639+
async fn rate_limit_bad_count() {
640+
simulate_rate_limit(&RateLimitTestParams {
641+
has_remaining_count: true,
642+
bad_remaining_count: true,
643+
..Default::default()
644+
})
645+
.await;
646+
}
647+
}

‎cpp-linter/src/run.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ mod test {
146146
use std::env;
147147

148148
#[tokio::test]
149-
async fn run() {
149+
async fn normal() {
150150
env::remove_var("GITHUB_OUTPUT"); // avoid writing to GH_OUT in parallel-running tests
151151
let result = run_main(vec![
152152
"cpp-linter".to_string(),
@@ -161,14 +161,14 @@ mod test {
161161
}
162162

163163
#[tokio::test]
164-
async fn run_version_command() {
164+
async fn version_command() {
165165
env::remove_var("GITHUB_OUTPUT"); // avoid writing to GH_OUT in parallel-running tests
166166
let result = run_main(vec!["cpp-linter".to_string(), "version".to_string()]).await;
167167
assert!(result.is_ok_and(|v| v == 0));
168168
}
169169

170170
#[tokio::test]
171-
async fn run_force_debug_output() {
171+
async fn force_debug_output() {
172172
env::remove_var("GITHUB_OUTPUT"); // avoid writing to GH_OUT in parallel-running tests
173173
let result = run_main(vec![
174174
"cpp-linter".to_string(),
@@ -182,7 +182,7 @@ mod test {
182182
}
183183

184184
#[tokio::test]
185-
async fn run_bad_version_input() {
185+
async fn bad_version_input() {
186186
env::remove_var("GITHUB_OUTPUT"); // avoid writing to GH_OUT in parallel-running tests
187187
let result = run_main(vec![
188188
"cpp-linter".to_string(),
@@ -193,4 +193,17 @@ mod test {
193193
.await;
194194
assert!(result.is_ok_and(|v| v == 1));
195195
}
196+
197+
#[tokio::test]
198+
async fn pre_commit_env() {
199+
env::remove_var("GITHUB_OUTPUT"); // avoid writing to GH_OUT in parallel-running tests
200+
env::set_var("PRE_COMMIT", "1");
201+
let result = run_main(vec![
202+
"cpp-linter".to_string(),
203+
"-l".to_string(),
204+
"false".to_string(),
205+
])
206+
.await;
207+
assert!(result.is_ok_and(|v| v == 1));
208+
}
196209
}

‎cpp-linter/tests/paginated_changed_files.rs

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ const SHA: &str = "DEADBEEF";
2222
const TOKEN: &str = "123456";
2323
const RESET_RATE_LIMIT_HEADER: &str = "x-ratelimit-reset";
2424
const REMAINING_RATE_LIMIT_HEADER: &str = "x-ratelimit-remaining";
25+
const MALFORMED_RESPONSE_PAYLOAD: &str = "{\"message\":\"Resource not accessible by integration\"}";
2526

26-
async fn get_paginated_changes(lib_root: &Path, event_type: EventType) {
27+
async fn get_paginated_changes(lib_root: &Path, event_type: EventType, fail_serialization: bool) {
2728
env::set_var("GITHUB_REPOSITORY", REPO);
2829
env::set_var("GITHUB_SHA", SHA);
2930
env::set_var("GITHUB_TOKEN", TOKEN);
@@ -84,68 +85,81 @@ async fn get_paginated_changes(lib_root: &Path, event_type: EventType) {
8485
} else {
8586
format!("{diff_end_point}/files")
8687
};
87-
for pg in 1..=2 {
88+
let pg_count = if fail_serialization { 1 } else { 2 };
89+
for pg in 1..=pg_count {
8890
let link = if pg == 1 {
8991
format!("<{}{pg_end_point}?page=2>; rel=\"next\"", server.url())
9092
} else {
9193
"".to_string()
9294
};
93-
mocks.push(
94-
server
95-
.mock("GET", pg_end_point.as_str())
96-
.match_header("Accept", "application/vnd.github.raw+json")
97-
.match_header("Authorization", format!("token {TOKEN}").as_str())
98-
.match_query(Matcher::UrlEncoded("page".to_string(), pg.to_string()))
99-
.with_header(REMAINING_RATE_LIMIT_HEADER, "50")
100-
.with_header(RESET_RATE_LIMIT_HEADER, reset_timestamp.as_str())
101-
.with_body_from_file(format!(
102-
"{asset_path}/{}_files_pg{pg}.json",
103-
if event_type == EventType::Push {
104-
"push"
105-
} else {
106-
"pull_request"
107-
}
108-
))
109-
.with_header("link", link.as_str())
110-
.create(),
111-
);
95+
let mut mock = server
96+
.mock("GET", pg_end_point.as_str())
97+
.match_header("Accept", "application/vnd.github.raw+json")
98+
.match_header("Authorization", format!("token {TOKEN}").as_str())
99+
.match_query(Matcher::UrlEncoded("page".to_string(), pg.to_string()))
100+
.with_header(REMAINING_RATE_LIMIT_HEADER, "50")
101+
.with_header(RESET_RATE_LIMIT_HEADER, reset_timestamp.as_str())
102+
.with_header("link", link.as_str());
103+
if fail_serialization {
104+
mock = mock.with_body(MALFORMED_RESPONSE_PAYLOAD);
105+
} else {
106+
mock = mock.with_body_from_file(format!(
107+
"{asset_path}/{}_files_pg{pg}.json",
108+
if event_type == EventType::Push {
109+
"push"
110+
} else {
111+
"pull_request"
112+
}
113+
));
114+
}
115+
mocks.push(mock.create());
112116
}
113117

114118
let file_filter = FileFilter::new(&[], vec!["cpp".to_string(), "hpp".to_string()]);
115-
let files = gh_client
116-
.get_list_of_changed_files(&file_filter)
117-
.await
118-
.unwrap();
119-
assert_eq!(files.len(), 2);
120-
for file in files {
121-
assert!(["src/demo.cpp", "src/demo.hpp"].contains(
122-
&file
123-
.name
124-
.as_path()
125-
.to_str()
126-
.expect("Failed to get file name from path")
127-
));
119+
let files = gh_client.get_list_of_changed_files(&file_filter).await;
120+
if let Ok(files) = files {
121+
// if !fail_serialization
122+
assert_eq!(files.len(), 2);
123+
for file in files {
124+
assert!(["src/demo.cpp", "src/demo.hpp"].contains(
125+
&file
126+
.name
127+
.as_path()
128+
.to_str()
129+
.expect("Failed to get file name from path")
130+
));
131+
}
128132
}
129133
for mock in mocks {
130134
mock.assert();
131135
}
132136
}
133137

134-
async fn test_get_changes(event_type: EventType) {
138+
async fn test_get_changes(event_type: EventType, fail_serialization: bool) {
135139
let tmp_dir = create_test_space(false);
136140
let lib_root = env::current_dir().unwrap();
137141
env::set_current_dir(tmp_dir.path()).unwrap();
138-
get_paginated_changes(&lib_root, event_type).await;
142+
get_paginated_changes(&lib_root, event_type, fail_serialization).await;
139143
env::set_current_dir(lib_root.as_path()).unwrap();
140144
drop(tmp_dir);
141145
}
142146

143147
#[tokio::test]
144148
async fn get_push_files_paginated() {
145-
test_get_changes(EventType::Push).await
149+
test_get_changes(EventType::Push, false).await
146150
}
147151

148152
#[tokio::test]
149153
async fn get_pr_files_paginated() {
150-
test_get_changes(EventType::PullRequest(42)).await
154+
test_get_changes(EventType::PullRequest(42), false).await
155+
}
156+
157+
#[tokio::test]
158+
async fn fail_push_files_paginated() {
159+
test_get_changes(EventType::Push, true).await
160+
}
161+
162+
#[tokio::test]
163+
async fn fail_pr_files_paginated() {
164+
test_get_changes(EventType::PullRequest(42), true).await
151165
}

0 commit comments

Comments
 (0)
Please sign in to comment.