Skip to content

Commit 11fc233

Browse files
committed
Create testsuite for GithubClient.
1 parent f69453e commit 11fc233

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)));
@@ -247,7 +251,7 @@ pub struct Issue {
247251
pub number: u64,
248252
#[serde(deserialize_with = "opt_string")]
249253
pub body: String,
250-
created_at: chrono::DateTime<Utc>,
254+
pub created_at: chrono::DateTime<Utc>,
251255
pub updated_at: chrono::DateTime<Utc>,
252256
/// The SHA for a merge commit.
253257
///
@@ -263,6 +267,7 @@ pub struct Issue {
263267
pub html_url: String,
264268
pub user: User,
265269
pub labels: Vec<Label>,
270+
pub milestone: Option<Milestone>,
266271
pub assignees: Vec<User>,
267272
/// Indicator if this is a pull request.
268273
///
@@ -329,6 +334,7 @@ impl ZulipGitHubReference {
329334

330335
#[derive(Debug, serde::Deserialize)]
331336
pub struct Comment {
337+
pub id: i64,
332338
#[serde(deserialize_with = "opt_string")]
333339
pub body: String,
334340
pub html_url: String,
@@ -475,12 +481,22 @@ impl Issue {
475481
self.state == IssueState::Open
476482
}
477483

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

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

855871
#[derive(Debug, serde::Deserialize)]
856872
pub struct Milestone {
857-
number: u64,
858-
title: String,
873+
pub number: u64,
874+
pub title: String,
859875
}
860876

861877
#[derive(Debug, serde::Deserialize)]
@@ -1064,6 +1080,15 @@ impl Repository {
10641080
|| filters.iter().any(|&(key, _)| key == "no")
10651081
|| is_pr && !include_labels.is_empty();
10661082

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

10841109
let result = client.get(&url);
10851110
if use_search_api {
1086-
let result = client
1111+
let mut result = client
10871112
.json::<IssueSearchResult>(result)
10881113
.await
10891114
.with_context(|| format!("failed to list issues from {}", url))?;
1115+
set_is_pr(&mut result.items);
10901116
issues.extend(result.items);
10911117
if issues.len() < result.total_count {
10921118
ordering.page += 1;
@@ -1097,7 +1123,8 @@ impl Repository {
10971123
issues = client
10981124
.json(result)
10991125
.await
1100-
.with_context(|| format!("failed to list issues from {}", url))?
1126+
.with_context(|| format!("failed to list issues from {}", url))?;
1127+
set_is_pr(&mut issues);
11011128
}
11021129

11031130
break;
@@ -1237,7 +1264,7 @@ impl Repository {
12371264
client: &GithubClient,
12381265
refname: &str,
12391266
sha: &str,
1240-
) -> anyhow::Result<()> {
1267+
) -> anyhow::Result<GitReference> {
12411268
let url = format!("{}/git/refs/{}", self.url(client), refname);
12421269
client
12431270
.json(client.patch(&url).json(&serde_json::json!({
@@ -1250,8 +1277,7 @@ impl Repository {
12501277
"{} failed to update reference {refname} to {sha}",
12511278
self.full_name
12521279
)
1253-
})?;
1254-
Ok(())
1280+
})
12551281
}
12561282

12571283
/// Returns a list of recent commits on the given branch.
@@ -1511,6 +1537,16 @@ impl Repository {
15111537
})?;
15121538
Ok(())
15131539
}
1540+
1541+
pub async fn get_pr(&self, client: &GithubClient, number: u64) -> anyhow::Result<Issue> {
1542+
let url = format!("{}/pulls/{number}", self.url(client));
1543+
let mut pr: Issue = client
1544+
.json(client.get(&url))
1545+
.await
1546+
.with_context(|| format!("{} failed to get pr {number}", self.full_name))?;
1547+
pr.pull_request = Some(PullRequestDetails {});
1548+
Ok(pr)
1549+
}
15141550
}
15151551

15161552
pub struct Query<'a> {
@@ -1812,12 +1848,14 @@ impl GithubClient {
18121848
let req = req
18131849
.build()
18141850
.with_context(|| format!("failed to build request {:?}", req_dbg))?;
1851+
let test_capture_info = test_record::capture_request(&req);
18151852
let resp = self.client.execute(req).await.context(req_dbg.clone())?;
18161853
let status = resp.status();
18171854
let body = resp
18181855
.bytes()
18191856
.await
18201857
.with_context(|| format!("failed to read response body {req_dbg}"))?;
1858+
test_record::record_request(test_capture_info, status, &body);
18211859
match status {
18221860
StatusCode::OK => Ok(Some(body)),
18231861
StatusCode::NOT_FOUND => Ok(None),
@@ -1970,6 +2008,7 @@ pub struct GitTreeEntry {
19702008
pub sha: String,
19712009
}
19722010

2011+
#[derive(Debug)]
19732012
pub struct RecentCommit {
19742013
pub title: String,
19752014
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)