Skip to content

Commit f9015e6

Browse files
committed
Create testsuite for GithubClient.
1 parent ad819a5 commit f9015e6

File tree

128 files changed

+31377
-9
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+31377
-9
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ You need to sign up for a free account, and also deal with configuring the GitHu
115115
* Secret: Enter a shared secret (some longish random text)
116116
* Events: "Send me everything"
117117

118+
## Tests
119+
120+
When possible, writing unittests is very helpful and one of the easiest ways to test.
121+
For more advanced testing, there is an integration test called `testsuite` which provides a testing environment for testing triagebot.
122+
At this time, there is one part to it:
123+
124+
* [`github_client`](tests/github_client/mod.rs) — Tests specifically targeting `GithubClient`.
125+
This sets up an HTTP server that mimics api.github.com and verifies the client's behavior.
126+
127+
Other parts may be added in the future, such as testing the database or the triagebot server itself.
128+
129+
The real GitHub API responses are recorded in JSON files that the tests can later replay to verify the behavior of triagebot.
130+
These recordings are enabled with the `TRIAGEBOT_TEST_RECORD_DIR` environment variable.
131+
See the documentation in `github_client` for the steps for setting up recording to write a test.
132+
118133
## License
119134

120135
Triagebot is distributed under the terms of both the MIT license and the

src/github.rs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::test_record;
12
use anyhow::{anyhow, Context};
23
use async_trait::async_trait;
34
use bytes::Bytes;
@@ -30,15 +31,18 @@ impl GithubClient {
3031
.build()
3132
.with_context(|| format!("building reqwest {}", req_dbg))?;
3233

34+
let test_capture_info = test_record::capture_request(&req);
3335
let mut resp = self.client.execute(req.try_clone().unwrap()).await?;
3436
if let Some(sleep) = Self::needs_retry(&resp).await {
3537
resp = self.retry(req, sleep, MAX_ATTEMPTS).await?;
3638
}
39+
let status = resp.status();
3740
let maybe_err = resp.error_for_status_ref().err();
3841
let body = resp
3942
.bytes()
4043
.await
4144
.with_context(|| format!("failed to read response body {req_dbg}"))?;
45+
test_record::record_request(test_capture_info, status, &body);
4246
if let Some(e) = maybe_err {
4347
return Err(anyhow::Error::new(e))
4448
.with_context(|| format!("response: {}", String::from_utf8_lossy(&body)));
@@ -248,7 +252,7 @@ pub struct Issue {
248252
pub number: u64,
249253
#[serde(deserialize_with = "opt_string")]
250254
pub body: String,
251-
created_at: chrono::DateTime<Utc>,
255+
pub created_at: chrono::DateTime<Utc>,
252256
pub updated_at: chrono::DateTime<Utc>,
253257
/// The SHA for a merge commit.
254258
///
@@ -264,6 +268,7 @@ pub struct Issue {
264268
pub html_url: String,
265269
pub user: User,
266270
pub labels: Vec<Label>,
271+
pub milestone: Option<Milestone>,
267272
pub assignees: Vec<User>,
268273
/// Indicator if this is a pull request.
269274
///
@@ -330,6 +335,7 @@ impl ZulipGitHubReference {
330335

331336
#[derive(Debug, serde::Deserialize)]
332337
pub struct Comment {
338+
pub id: i64,
333339
#[serde(deserialize_with = "opt_string")]
334340
pub body: String,
335341
pub html_url: String,
@@ -476,12 +482,22 @@ impl Issue {
476482
self.state == IssueState::Open
477483
}
478484

479-
pub async fn get_comment(&self, client: &GithubClient, id: usize) -> anyhow::Result<Comment> {
485+
pub async fn get_comment(&self, client: &GithubClient, id: u64) -> anyhow::Result<Comment> {
480486
let comment_url = format!("{}/issues/comments/{}", self.repository().url(client), id);
481487
let comment = client.json(client.get(&comment_url)).await?;
482488
Ok(comment)
483489
}
484490

491+
pub async fn get_comments(&self, client: &GithubClient) -> anyhow::Result<Vec<Comment>> {
492+
let comment_url = format!(
493+
"{}/issues/{}/comments",
494+
self.repository().url(client),
495+
self.number
496+
);
497+
let comments = client.json(client.get(&comment_url)).await?;
498+
Ok(comments)
499+
}
500+
485501
pub async fn edit_body(&self, client: &GithubClient, body: &str) -> anyhow::Result<()> {
486502
let edit_url = format!("{}/issues/{}", self.repository().url(client), self.number);
487503
#[derive(serde::Serialize)]
@@ -855,8 +871,8 @@ struct MilestoneCreateBody<'a> {
855871

856872
#[derive(Debug, serde::Deserialize)]
857873
pub struct Milestone {
858-
number: u64,
859-
title: String,
874+
pub number: u64,
875+
pub title: String,
860876
}
861877

862878
#[derive(Debug, serde::Deserialize)]
@@ -1066,6 +1082,15 @@ impl Repository {
10661082
|| filters.iter().any(|&(key, _)| key == "no")
10671083
|| is_pr && !include_labels.is_empty();
10681084

1085+
let set_is_pr = |issues: &mut [Issue]| {
1086+
if !is_pr {
1087+
return;
1088+
}
1089+
for issue in issues {
1090+
issue.pull_request = Some(PullRequestDetails {});
1091+
}
1092+
};
1093+
10691094
// If there are more than `per_page` of issues, we need to paginate
10701095
let mut issues = vec![];
10711096
loop {
@@ -1085,10 +1110,11 @@ impl Repository {
10851110

10861111
let result = client.get(&url);
10871112
if use_search_api {
1088-
let result = client
1113+
let mut result = client
10891114
.json::<IssueSearchResult>(result)
10901115
.await
10911116
.with_context(|| format!("failed to list issues from {}", url))?;
1117+
set_is_pr(&mut result.items);
10921118
issues.extend(result.items);
10931119
if issues.len() < result.total_count {
10941120
ordering.page += 1;
@@ -1099,7 +1125,8 @@ impl Repository {
10991125
issues = client
11001126
.json(result)
11011127
.await
1102-
.with_context(|| format!("failed to list issues from {}", url))?
1128+
.with_context(|| format!("failed to list issues from {}", url))?;
1129+
set_is_pr(&mut issues);
11031130
}
11041131

11051132
break;
@@ -1239,7 +1266,7 @@ impl Repository {
12391266
client: &GithubClient,
12401267
refname: &str,
12411268
sha: &str,
1242-
) -> anyhow::Result<()> {
1269+
) -> anyhow::Result<GitReference> {
12431270
let url = format!("{}/git/refs/{}", self.url(client), refname);
12441271
client
12451272
.json(client.patch(&url).json(&serde_json::json!({
@@ -1252,8 +1279,7 @@ impl Repository {
12521279
"{} failed to update reference {refname} to {sha}",
12531280
self.full_name
12541281
)
1255-
})?;
1256-
Ok(())
1282+
})
12571283
}
12581284

12591285
/// Returns a list of recent commits on the given branch.
@@ -1563,6 +1589,16 @@ impl Repository {
15631589
})?;
15641590
Ok(())
15651591
}
1592+
1593+
pub async fn get_pr(&self, client: &GithubClient, number: u64) -> anyhow::Result<Issue> {
1594+
let url = format!("{}/pulls/{number}", self.url(client));
1595+
let mut pr: Issue = client
1596+
.json(client.get(&url))
1597+
.await
1598+
.with_context(|| format!("{} failed to get pr {number}", self.full_name))?;
1599+
pr.pull_request = Some(PullRequestDetails {});
1600+
Ok(pr)
1601+
}
15661602
}
15671603

15681604
pub struct Query<'a> {
@@ -1864,12 +1900,14 @@ impl GithubClient {
18641900
let req = req
18651901
.build()
18661902
.with_context(|| format!("failed to build request {:?}", req_dbg))?;
1903+
let test_capture_info = test_record::capture_request(&req);
18671904
let resp = self.client.execute(req).await.context(req_dbg.clone())?;
18681905
let status = resp.status();
18691906
let body = resp
18701907
.bytes()
18711908
.await
18721909
.with_context(|| format!("failed to read response body {req_dbg}"))?;
1910+
test_record::record_request(test_capture_info, status, &body);
18731911
match status {
18741912
StatusCode::OK => Ok(Some(body)),
18751913
StatusCode::NOT_FOUND => Ok(None),
@@ -2022,6 +2060,7 @@ pub struct GitTreeEntry {
20222060
pub sha: String,
20232061
}
20242062

2063+
#[derive(Debug)]
20252064
pub struct RecentCommit {
20262065
pub title: String,
20272066
pub pr_num: Option<i32>,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod payload;
2626
pub mod rfcbot;
2727
pub mod team;
2828
mod team_data;
29+
pub mod test_record;
2930
pub mod triage;
3031
pub mod zulip;
3132

0 commit comments

Comments
 (0)