From 2b9c820c6ba1c48d166238bf4d63d0f98a9df806 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 7 Jun 2023 10:16:04 -0700 Subject: [PATCH 1/3] Make it so that the API URLs are configurable for testing --- src/actions.rs | 3 +- src/github.rs | 150 +++++++++++++++++++++--------------- src/handlers/docs_update.rs | 3 +- src/main.rs | 4 +- src/team_data.rs | 3 +- 5 files changed, 93 insertions(+), 70 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index a87d7fb8..1907195c 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; -use reqwest::Client; use serde::{Deserialize, Serialize}; use tera::{Context, Tera}; @@ -87,7 +86,7 @@ pub fn to_human(d: DateTime) -> String { #[async_trait] impl<'a> Action for Step<'a> { async fn call(&self) -> anyhow::Result { - let gh = GithubClient::new_with_default_token(Client::new()); + let gh = GithubClient::new_from_env(); // retrieve all Rust compiler meetings // from today for 7 days diff --git a/src/github.rs b/src/github.rs index e3aeddde..1fb3184a 100644 --- a/src/github.rs +++ b/src/github.rs @@ -118,7 +118,7 @@ impl GithubClient { .client .execute( self.client - .get("https://api.github.com/rate_limit") + .get(&format!("{}/rate_limit", self.api_url)) .configure(self) .build() .unwrap(), @@ -171,7 +171,9 @@ impl GithubClient { impl User { pub async fn current(client: &GithubClient) -> anyhow::Result { - client.json(client.get("https://api.github.com/user")).await + client + .json(client.get(&format!("{}/user", client.api_url))) + .await } pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result { @@ -396,16 +398,16 @@ impl fmt::Display for IssueRepository { } impl IssueRepository { - fn url(&self) -> String { + fn url(&self, client: &GithubClient) -> String { format!( - "https://api.github.com/repos/{}/{}", - self.organization, self.repository + "{}/repos/{}/{}", + client.api_url, self.organization, self.repository ) } async fn has_label(&self, client: &GithubClient, label: &str) -> anyhow::Result { #[allow(clippy::redundant_pattern_matching)] - let url = format!("{}/labels/{}", self.url(), label); + let url = format!("{}/labels/{}", self.url(client), label); match client.send_req(client.get(&url)).await { Ok(_) => Ok(true), Err(e) => { @@ -475,13 +477,13 @@ impl Issue { } pub async fn get_comment(&self, client: &GithubClient, id: usize) -> anyhow::Result { - let comment_url = format!("{}/issues/comments/{}", self.repository().url(), id); + let comment_url = format!("{}/issues/comments/{}", self.repository().url(client), id); let comment = client.json(client.get(&comment_url)).await?; Ok(comment) } pub async fn edit_body(&self, client: &GithubClient, body: &str) -> anyhow::Result<()> { - let edit_url = format!("{}/issues/{}", self.repository().url(), self.number); + let edit_url = format!("{}/issues/{}", self.repository().url(client), self.number); #[derive(serde::Serialize)] struct ChangedIssue<'a> { body: &'a str, @@ -499,7 +501,7 @@ impl Issue { id: usize, new_body: &str, ) -> anyhow::Result<()> { - let comment_url = format!("{}/issues/comments/{}", self.repository().url(), id); + let comment_url = format!("{}/issues/comments/{}", self.repository().url(client), id); #[derive(serde::Serialize)] struct NewComment<'a> { body: &'a str, @@ -520,8 +522,13 @@ impl Issue { struct PostComment<'a> { body: &'a str, } + let comments_path = self + .comments_url + .strip_prefix("https://api.github.com") + .expect("expected api host"); + let comments_url = format!("{}{comments_path}", client.api_url); client - .send_req(client.post(&self.comments_url).json(&PostComment { body })) + .send_req(client.post(&comments_url).json(&PostComment { body })) .await .context("failed to post comment")?; Ok(()) @@ -532,7 +539,7 @@ impl Issue { // DELETE /repos/:owner/:repo/issues/:number/labels/{name} let url = format!( "{repo_url}/issues/{number}/labels/{name}", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number, name = label, ); @@ -564,7 +571,7 @@ impl Issue { // repo_url = https://api.github.com/repos/Codertocat/Hello-World let url = format!( "{repo_url}/issues/{number}/labels", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -631,7 +638,7 @@ impl Issue { log::info!("remove {:?} assignees for {}", selection, self.global_id()); let url = format!( "{repo_url}/issues/{number}/assignees", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -671,7 +678,7 @@ impl Issue { log::info!("add_assignee {} for {}", user, self.global_id()); let url = format!( "{repo_url}/issues/{number}/assignees", - repo_url = self.repository().url(), + repo_url = self.repository().url(client), number = self.number ); @@ -717,7 +724,7 @@ impl Issue { title ); - let create_url = format!("{}/milestones", self.repository().url()); + let create_url = format!("{}/milestones", self.repository().url(client)); let resp = client .send_req( client @@ -729,7 +736,7 @@ impl Issue { // fine, it just means the milestone was already created. log::trace!("Created milestone: {:?}", resp); - let list_url = format!("{}/milestones", self.repository().url()); + let list_url = format!("{}/milestones", self.repository().url(client)); let milestone_list: Vec = client.json(client.get(&list_url)).await?; let milestone_no = if let Some(milestone) = milestone_list.iter().find(|v| v.title == title) { @@ -746,7 +753,7 @@ impl Issue { struct SetMilestone { milestone: u64, } - let url = format!("{}/issues/{}", self.repository().url(), self.number); + let url = format!("{}/issues/{}", self.repository().url(client), self.number); client .send_req(client.patch(&url).json(&SetMilestone { milestone: milestone_no, @@ -757,7 +764,7 @@ impl Issue { } pub async fn close(&self, client: &GithubClient) -> anyhow::Result<()> { - let edit_url = format!("{}/issues/{}", self.repository().url(), self.number); + let edit_url = format!("{}/issues/{}", self.repository().url(client), self.number); #[derive(serde::Serialize)] struct CloseIssue<'a> { state: &'a str, @@ -783,7 +790,7 @@ impl Issue { let mut req = client.get(&format!( "{}/compare/{}...{}", - self.repository().url(), + self.repository().url(client), before, after )); @@ -805,7 +812,7 @@ impl Issue { loop { let req = client.get(&format!( "{}/pulls/{}/commits?page={page}&per_page=100", - self.repository().url(), + self.repository().url(client), self.number )); @@ -827,7 +834,7 @@ impl Issue { let req = client.get(&format!( "{}/pulls/{}/files", - self.repository().url(), + self.repository().url(client), self.number )); Ok(client.json(req).await?) @@ -1000,11 +1007,8 @@ struct Ordering<'a> { } impl Repository { - const GITHUB_API_URL: &'static str = "https://api.github.com"; - const GITHUB_GRAPHQL_API_URL: &'static str = "https://api.github.com/graphql"; - - fn url(&self) -> String { - format!("{}/repos/{}", Repository::GITHUB_API_URL, self.full_name) + fn url(&self, client: &GithubClient) -> String { + format!("{}/repos/{}", client.api_url, self.full_name) } pub fn owner(&self) -> &str { @@ -1066,11 +1070,17 @@ impl Repository { let mut issues = vec![]; loop { let url = if use_search_api { - self.build_search_issues_url(&filters, include_labels, exclude_labels, ordering) + self.build_search_issues_url( + client, + &filters, + include_labels, + exclude_labels, + ordering, + ) } else if is_pr { - self.build_pulls_url(&filters, include_labels, ordering) + self.build_pulls_url(client, &filters, include_labels, ordering) } else { - self.build_issues_url(&filters, include_labels, ordering) + self.build_issues_url(client, &filters, include_labels, ordering) }; let result = client.get(&url); @@ -1099,24 +1109,27 @@ impl Repository { fn build_issues_url( &self, + client: &GithubClient, filters: &Vec<(&str, &str)>, include_labels: &Vec<&str>, ordering: Ordering<'_>, ) -> String { - self.build_endpoint_url("issues", filters, include_labels, ordering) + self.build_endpoint_url(client, "issues", filters, include_labels, ordering) } fn build_pulls_url( &self, + client: &GithubClient, filters: &Vec<(&str, &str)>, include_labels: &Vec<&str>, ordering: Ordering<'_>, ) -> String { - self.build_endpoint_url("pulls", filters, include_labels, ordering) + self.build_endpoint_url(client, "pulls", filters, include_labels, ordering) } fn build_endpoint_url( &self, + client: &GithubClient, endpoint: &str, filters: &Vec<(&str, &str)>, include_labels: &Vec<&str>, @@ -1139,15 +1152,13 @@ impl Repository { .join("&"); format!( "{}/repos/{}/{}?{}", - Repository::GITHUB_API_URL, - self.full_name, - endpoint, - filters + client.api_url, self.full_name, endpoint, filters ) } fn build_search_issues_url( &self, + client: &GithubClient, filters: &Vec<(&str, &str)>, include_labels: &Vec<&str>, exclude_labels: &Vec<&str>, @@ -1172,7 +1183,7 @@ impl Repository { .join("+"); format!( "{}/search/issues?q={}&sort={}&order={}&per_page={}&page={}", - Repository::GITHUB_API_URL, + client.api_url, filters, ordering.sort, ordering.direction, @@ -1183,7 +1194,7 @@ impl Repository { /// Retrieves a git commit for the given SHA. pub async fn git_commit(&self, client: &GithubClient, sha: &str) -> anyhow::Result { - let url = format!("{}/git/commits/{sha}", self.url()); + let url = format!("{}/git/commits/{sha}", self.url(client)); client .json(client.get(&url)) .await @@ -1198,7 +1209,7 @@ impl Repository { parents: &[&str], tree: &str, ) -> anyhow::Result { - let url = format!("{}/git/commits", self.url()); + let url = format!("{}/git/commits", self.url(client)); client .json(client.post(&url).json(&serde_json::json!({ "message": message, @@ -1215,7 +1226,7 @@ impl Repository { client: &GithubClient, refname: &str, ) -> anyhow::Result { - let url = format!("{}/git/ref/{}", self.url(), refname); + let url = format!("{}/git/ref/{}", self.url(client), refname); client .json(client.get(&url)) .await @@ -1229,7 +1240,7 @@ impl Repository { refname: &str, sha: &str, ) -> anyhow::Result { - let url = format!("{}/git/refs/{}", self.url(), refname); + let url = format!("{}/git/refs/{}", self.url(client), refname); client .json(client.patch(&url).json(&serde_json::json!({ "sha": sha, @@ -1280,7 +1291,7 @@ impl Repository { let query = RecentCommits::build(args.clone()); let data = client .json::>( - client.post(Repository::GITHUB_GRAPHQL_API_URL).json(&query), + client.post(&client.graphql_url).json(&query), ) .await .with_context(|| { @@ -1419,7 +1430,7 @@ impl Repository { base_tree: &str, tree: &[GitTreeEntry], ) -> anyhow::Result { - let url = format!("{}/git/trees", self.url()); + let url = format!("{}/git/trees", self.url(client)); client .json(client.post(&url).json(&serde_json::json!({ "base_tree": base_tree, @@ -1444,7 +1455,7 @@ impl Repository { path: &str, refname: Option<&str>, ) -> anyhow::Result { - let mut url = format!("{}/contents/{}", self.url(), path); + let mut url = format!("{}/contents/{}", self.url(client), path); if let Some(refname) = refname { url.push_str("?ref="); url.push_str(refname); @@ -1466,7 +1477,7 @@ impl Repository { base: &str, body: &str, ) -> anyhow::Result { - let url = format!("{}/pulls", self.url()); + let url = format!("{}/pulls", self.url(client)); let mut issue: Issue = client .json(client.post(&url).json(&serde_json::json!({ "title": title, @@ -1489,7 +1500,7 @@ impl Repository { /// /// **Warning**: This will to a force update if there are conflicts. pub async fn merge_upstream(&self, client: &GithubClient, branch: &str) -> anyhow::Result<()> { - let url = format!("{}/merge-upstream", self.url()); + let url = format!("{}/merge-upstream", self.url(client)); let merge_error = match client .send_req(client.post(&url).json(&serde_json::json!({ "branch": branch, @@ -1808,15 +1819,32 @@ fn get_token_from_git_config() -> anyhow::Result { pub struct GithubClient { token: String, client: Client, + api_url: String, + graphql_url: String, + raw_url: String, } impl GithubClient { - pub fn new(client: Client, token: String) -> Self { - GithubClient { client, token } + pub fn new(token: String, api_url: String, graphql_url: String, raw_url: String) -> Self { + GithubClient { + client: Client::new(), + token, + api_url, + graphql_url, + raw_url, + } } - pub fn new_with_default_token(client: Client) -> Self { - Self::new(client, default_token_from_env()) + pub fn new_from_env() -> Self { + Self::new( + default_token_from_env(), + std::env::var("GITHUB_API_URL") + .unwrap_or_else(|_| "https://api.github.com".to_string()), + std::env::var("GITHUB_GRAPHQL_API_URL") + .unwrap_or_else(|_| "https://api.github.com/graphql".to_string()), + std::env::var("GITHUB_RAW_URL") + .unwrap_or_else(|_| "https://raw.githubusercontent.com".to_string()), + ) } pub fn raw(&self) -> &Client { @@ -1829,10 +1857,7 @@ impl GithubClient { branch: &str, path: &str, ) -> anyhow::Result> { - let url = format!( - "https://raw.githubusercontent.com/{}/{}/{}", - repo, branch, path - ); + let url = format!("{}/{repo}/{branch}/{path}", self.raw_url); let req = self.get(&url); let req_dbg = format!("{:?}", req); let req = req @@ -1894,8 +1919,8 @@ impl GithubClient { pub async fn rust_commit(&self, sha: &str) -> Option { let req = self.get(&format!( - "https://api.github.com/repos/rust-lang/rust/commits/{}", - sha + "{}/repos/rust-lang/rust/commits/{sha}", + self.api_url )); match self.json(req).await { Ok(r) => Some(r), @@ -1908,7 +1933,10 @@ impl GithubClient { /// This does not retrieve all of them, only the last several. pub async fn bors_commits(&self) -> Vec { - let req = self.get("https://api.github.com/repos/rust-lang/rust/commits?author=bors"); + let req = self.get(&format!( + "{}/repos/rust-lang/rust/commits?author=bors", + self.api_url + )); match self.json(req).await { Ok(r) => r, Err(e) => { @@ -1923,9 +1951,7 @@ impl GithubClient { pub async fn is_new_contributor(&self, repo: &Repository, author: &str) -> bool { let url = format!( "{}/repos/{}/commits?author={}", - Repository::GITHUB_API_URL, - repo.full_name, - author, + self.api_url, repo.full_name, author, ); let req = self.get(&url); match self.json::>(req).await { @@ -1947,7 +1973,7 @@ impl GithubClient { /// /// The `full_name` should be something like `rust-lang/rust`. pub async fn repository(&self, full_name: &str) -> anyhow::Result { - let req = self.get(&format!("{}/repos/{full_name}", Repository::GITHUB_API_URL)); + let req = self.get(&format!("{}/repos/{full_name}", self.api_url)); self.json(req) .await .with_context(|| format!("{} failed to get repo", full_name)) @@ -2046,7 +2072,7 @@ impl IssuesQuery for LeastRecentlyReviewedPullRequests { }; loop { let query = queries::LeastRecentlyReviewedPullRequests::build(args.clone()); - let req = client.post(Repository::GITHUB_GRAPHQL_API_URL); + let req = client.post(&client.graphql_url); let req = req.json(&query); let data: cynic::GraphQlResponse = @@ -2180,7 +2206,7 @@ async fn project_items_by_status( let mut all_items = vec![]; loop { let query = project_items::Query::build(args.clone()); - let req = client.post(Repository::GITHUB_GRAPHQL_API_URL); + let req = client.post(&client.graphql_url); let req = req.json(&query); let data: cynic::GraphQlResponse = client.json(req).await?; diff --git a/src/handlers/docs_update.rs b/src/handlers/docs_update.rs index 215e88a0..d2ef8e7d 100644 --- a/src/handlers/docs_update.rs +++ b/src/handlers/docs_update.rs @@ -5,7 +5,6 @@ use crate::github::{self, GitTreeEntry, GithubClient, Issue, Repository}; use anyhow::Context; use anyhow::Result; use cron::Schedule; -use reqwest::Client; use std::fmt::Write; use std::str::FromStr; @@ -63,7 +62,7 @@ pub async fn handle_job() -> Result<()> { } pub async fn docs_update() -> Result> { - let gh = GithubClient::new_with_default_token(Client::new()); + let gh = GithubClient::new_from_env(); let work_repo = gh.repository(WORK_REPO).await?; work_repo .merge_upstream(&gh, &work_repo.default_branch) diff --git a/src/main.rs b/src/main.rs index 74aa06d6..c33f0b76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use anyhow::Context as _; use futures::future::FutureExt; use futures::StreamExt; use hyper::{header, Body, Request, Response, Server, StatusCode}; -use reqwest::Client; use route_recognizer::Router; use std::{env, net::SocketAddr, sync::Arc}; use tokio::{task, time}; @@ -239,8 +238,7 @@ async fn run_server(addr: SocketAddr) -> anyhow::Result<()> { .await .context("database migrations")?; - let client = Client::new(); - let gh = github::GithubClient::new_with_default_token(client.clone()); + let gh = github::GithubClient::new_from_env(); let oc = octocrab::OctocrabBuilder::new() .personal_token(github::default_token_from_env()) .build() diff --git a/src/team_data.rs b/src/team_data.rs index 4f7e1b15..d58ff408 100644 --- a/src/team_data.rs +++ b/src/team_data.rs @@ -4,7 +4,8 @@ use rust_team_data::v1::{Teams, ZulipMapping, BASE_URL}; use serde::de::DeserializeOwned; async fn by_url(client: &GithubClient, path: &str) -> anyhow::Result { - let url = format!("{}{}", BASE_URL, path); + let base = std::env::var("TEAMS_API_URL").unwrap_or(BASE_URL.to_string()); + let url = format!("{}{}", base, path); for _ in 0i32..3 { let map: Result = client.json(client.raw().get(&url)).await; match map { From 44b0496d0edf3a472b74d56e8fd3f1addb13fa1a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 7 Jun 2023 16:30:46 -0700 Subject: [PATCH 2/3] Create testsuite for GithubClient. --- README.md | 15 + src/github.rs | 52 +- src/lib.rs | 1 + src/test_record.rs | 231 ++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...s_triagebot-test_issues_115_assignees.json | 116 + ...-repos_ehuss_triagebot-test_pulls_115.json | 390 +++ ...s_triagebot-test_issues_115_assignees.json | 136 + ...-repos_ehuss_triagebot-test_pulls_115.json | 410 +++ ...s_triagebot-test_issues_115_assignees.json | 136 + ...-repos_ehuss_triagebot-test_pulls_115.json | 410 +++ ...s_triagebot-test_issues_115_assignees.json | 136 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...uss_triagebot-test_labels_enhancement.json | 17 + ...huss_triagebot-test_issues_115_labels.json | 19 + ...-repos_ehuss_triagebot-test_pulls_115.json | 360 +++ ..._triagebot-test_labels_does-not-exist.json | 12 + .../00-GET-repos_rust-lang_rust_commits.json | 2530 +++++++++++++++++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 386 +++ ...repos_ehuss_triagebot-test_issues_115.json | 132 + ...-repos_ehuss_triagebot-test_pulls_115.json | 386 +++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...huss_triagebot-test_pulls_115_commits.json | 89 + ...huss_triagebot-test_pulls_115_commits.json | 9 + .../00-GET-repos_ehuss_rust.json | 365 +++ .../01-POST-repos_ehuss_rust_git_commits.json | 42 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...d908ac31a6bccc4a0bba0497d3626756dacd7.json | 9 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...repos_ehuss_triagebot-test_issues_115.json | 96 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...gebot-test_issues_comments_1581325459.json | 52 + ...gebot-test_issues_comments_1581325459.json | 52 + ...gebot-test_issues_comments_1581325459.json | 52 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ..._ehuss_triagebot-test_pulls_115_files.json | 22 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...gebot-test_issues_comments_1581325459.json | 52 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...ss_triagebot-test_issues_115_comments.json | 54 + .../00-GET-repos_rust-lang_rust.json | 160 ++ .../01-GET-repos_rust-lang_rust_issues.json | 431 +++ .../00-GET-repos_rust-lang_rust.json | 160 ++ .../01-GET-search_issues.json | 614 ++++ .../00-GET-repos_rust-lang_rust.json | 160 ++ ...s_rust-lang_rust_git_ref_heads_stable.json | 18 + .../00-GET-repos_rust-lang_rust.json | 148 + ...cccbe4f345c0f0785ce860788580c3e2a29f5.json | 47 + .../00-GET-repos_rust-lang_rust.json | 148 + .../01-GET-repos_rust-lang_rust_commits.json | 9 + .../02-GET-repos_rust-lang_rust_commits.json | 2380 ++++++++++++++++ .../00-GET-repos_rust-lang_rust.json | 160 ++ .../01-GET-repos_rust-lang_rust_issues.json | 431 +++ .../00-GET-repos_ehuss_rust.json | 365 +++ ...-POST-repos_ehuss_rust_merge-upstream.json | 13 + tests/github_client/mod.rs | 860 ++++++ .../new_pr/00-GET-repos_ehuss_rust.json | 365 +++ .../01-POST-repos_ehuss_rust_pulls.json | 362 +++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...ss_triagebot-test_issues_115_comments.json | 52 + ...ss_triagebot-test_issues_115_comments.json | 98 + ...agebot-test_raw-file_docs_example_txt.json | 9 + .../00-GET-repos_rust-lang_rust.json | 148 + .../recent_commits/01-POST-graphql.json | 1938 +++++++++++++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...s_triagebot-test_issues_115_assignees.json | 76 + ...s_triagebot-test_issues_115_assignees.json | 116 + ...s_triagebot-test_issues_115_assignees.json | 136 + ...-repos_ehuss_triagebot-test_pulls_115.json | 410 +++ ...s_triagebot-test_issues_115_assignees.json | 76 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...s_triagebot-test_issues_115_assignees.json | 116 + ...s_triagebot-test_issues_115_assignees.json | 136 + ...-repos_ehuss_triagebot-test_pulls_115.json | 410 +++ ...s_triagebot-test_issues_115_assignees.json | 116 + ...-repos_ehuss_triagebot-test_pulls_115.json | 390 +++ ...s_triagebot-test_issues_115_assignees.json | 136 + ...s_triagebot-test_issues_115_assignees.json | 116 + ...-repos_ehuss_triagebot-test_pulls_115.json | 390 +++ ...s_triagebot-test_issues_115_assignees.json | 76 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 360 +++ ...ot-test_issues_115_labels_enhancement.json | 9 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ .../00-GET-repos_rust-lang_rust.json | 148 + ...2db0e87d8adccc9a83a47795c9411b1455855.json | 123 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...s_triagebot-test_issues_115_assignees.json | 116 + ...s_triagebot-test_issues_115_assignees.json | 116 + ...-repos_ehuss_triagebot-test_pulls_115.json | 390 +++ ...s_triagebot-test_issues_115_assignees.json | 136 + ...s_triagebot-test_issues_115_assignees.json | 116 + ...-repos_ehuss_triagebot-test_pulls_115.json | 390 +++ ...s_triagebot-test_issues_115_assignees.json | 116 + .../00-GET-repos_ehuss_triagebot-test.json | 128 + ...-repos_ehuss_triagebot-test_pulls_115.json | 350 +++ ...repos_ehuss_triagebot-test_milestones.json | 19 + ...repos_ehuss_triagebot-test_milestones.json | 47 + ...repos_ehuss_triagebot-test_issues_115.json | 132 + ...-repos_ehuss_triagebot-test_pulls_115.json | 386 +++ ...repos_ehuss_triagebot-test_milestones.json | 45 + ...repos_ehuss_triagebot-test_milestones.json | 84 + ...repos_ehuss_triagebot-test_issues_115.json | 132 + ...-repos_ehuss_triagebot-test_pulls_115.json | 386 +++ .../00-GET-repos_rust-lang_rust.json | 160 ++ ...-lang_rust_contents_src_doc_reference.json | 25 + .../02-GET-repos_rust-lang_reference.json | 160 ++ .../00-GET-repos_ehuss_rust.json | 356 +++ ...ehuss_rust_git_refs_heads_docs-update.json | 18 + .../update_tree/00-GET-repos_ehuss_rust.json | 365 +++ .../01-POST-repos_ehuss_rust_git_trees.json | 240 ++ tests/github_client/user/00-GET-user.json | 42 + tests/testsuite.rs | 349 +++ 128 files changed, 31375 insertions(+), 6 deletions(-) create mode 100644 src/test_record.rs create mode 100644 tests/github_client/add_assignee/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/add_assignee/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_assignee/02-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/add_assignee/03-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_assignee/04-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/add_assignee/05-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_assignee/06-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/add_assignee/07-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_assignee/08-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/add_labels/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/add_labels/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_labels/02-GET-repos_ehuss_triagebot-test_labels_enhancement.json create mode 100644 tests/github_client/add_labels/03-POST-repos_ehuss_triagebot-test_issues_115_labels.json create mode 100644 tests/github_client/add_labels/04-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/add_labels/05-GET-repos_ehuss_triagebot-test_labels_does-not-exist.json create mode 100644 tests/github_client/bors_commits/00-GET-repos_rust-lang_rust_commits.json create mode 100644 tests/github_client/close/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/close/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/close/02-PATCH-repos_ehuss_triagebot-test_issues_115.json create mode 100644 tests/github_client/close/03-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/commits/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/commits/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/commits/02-GET-repos_ehuss_triagebot-test_pulls_115_commits.json create mode 100644 tests/github_client/commits/03-GET-repos_ehuss_triagebot-test_pulls_115_commits.json create mode 100644 tests/github_client/create_commit/00-GET-repos_ehuss_rust.json create mode 100644 tests/github_client/create_commit/01-POST-repos_ehuss_rust_git_commits.json create mode 100644 tests/github_client/diff/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/diff/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/diff/02-GET-repos_ehuss_triagebot-test_compare_9a00ee2312071126101bb900f363210d6f3d1402___f39d908ac31a6bccc4a0bba0497d3626756dacd7.json create mode 100644 tests/github_client/edit_body/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/edit_body/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/edit_body/02-PATCH-repos_ehuss_triagebot-test_issues_115.json create mode 100644 tests/github_client/edit_body/03-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/edit_comment/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/edit_comment/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/edit_comment/02-GET-repos_ehuss_triagebot-test_issues_comments_1581325459.json create mode 100644 tests/github_client/edit_comment/03-PATCH-repos_ehuss_triagebot-test_issues_comments_1581325459.json create mode 100644 tests/github_client/edit_comment/04-GET-repos_ehuss_triagebot-test_issues_comments_1581325459.json create mode 100644 tests/github_client/files/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/files/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/files/02-GET-repos_ehuss_triagebot-test_pulls_115_files.json create mode 100644 tests/github_client/get_comment/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/get_comment/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/get_comment/02-GET-repos_ehuss_triagebot-test_issues_comments_1581325459.json create mode 100644 tests/github_client/get_comments/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/get_comments/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/get_comments/02-GET-repos_ehuss_triagebot-test_issues_115_comments.json create mode 100644 tests/github_client/get_issues_no_search/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/get_issues_no_search/01-GET-repos_rust-lang_rust_issues.json create mode 100644 tests/github_client/get_issues_with_search/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/get_issues_with_search/01-GET-search_issues.json create mode 100644 tests/github_client/get_reference/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/get_reference/01-GET-repos_rust-lang_rust_git_ref_heads_stable.json create mode 100644 tests/github_client/git_commit/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/git_commit/01-GET-repos_rust-lang_rust_git_commits_109cccbe4f345c0f0785ce860788580c3e2a29f5.json create mode 100644 tests/github_client/is_new_contributor/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/is_new_contributor/01-GET-repos_rust-lang_rust_commits.json create mode 100644 tests/github_client/is_new_contributor/02-GET-repos_rust-lang_rust_commits.json create mode 100644 tests/github_client/issue_properties/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/issue_properties/01-GET-repos_rust-lang_rust_issues.json create mode 100644 tests/github_client/merge_upstream/00-GET-repos_ehuss_rust.json create mode 100644 tests/github_client/merge_upstream/01-POST-repos_ehuss_rust_merge-upstream.json create mode 100644 tests/github_client/mod.rs create mode 100644 tests/github_client/new_pr/00-GET-repos_ehuss_rust.json create mode 100644 tests/github_client/new_pr/01-POST-repos_ehuss_rust_pulls.json create mode 100644 tests/github_client/post_comment/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/post_comment/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/post_comment/02-POST-repos_ehuss_triagebot-test_issues_115_comments.json create mode 100644 tests/github_client/post_comment/03-GET-repos_ehuss_triagebot-test_issues_115_comments.json create mode 100644 tests/github_client/raw_file/00-GET-ehuss_triagebot-test_raw-file_docs_example_txt.json create mode 100644 tests/github_client/recent_commits/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/recent_commits/01-POST-graphql.json create mode 100644 tests/github_client/remove_assignees/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/remove_assignees/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/02-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/03-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/04-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/05-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/06-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/07-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/08-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/09-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/10-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/11-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/12-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/13-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/14-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/15-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_assignees/16-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/remove_assignees/17-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_label/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/remove_label/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/remove_label/02-DELETE-repos_ehuss_triagebot-test_issues_115_labels_enhancement.json create mode 100644 tests/github_client/remove_label/03-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/repository/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/rust_commit/00-GET-repos_rust-lang_rust_commits_7632db0e87d8adccc9a83a47795c9411b1455855.json create mode 100644 tests/github_client/set_assignee/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/set_assignee/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/set_assignee/02-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/set_assignee/03-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/set_assignee/04-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/set_assignee/05-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/set_assignee/06-DELETE-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/set_assignee/07-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/set_assignee/08-POST-repos_ehuss_triagebot-test_issues_115_assignees.json create mode 100644 tests/github_client/set_milestone/00-GET-repos_ehuss_triagebot-test.json create mode 100644 tests/github_client/set_milestone/01-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/set_milestone/02-POST-repos_ehuss_triagebot-test_milestones.json create mode 100644 tests/github_client/set_milestone/03-GET-repos_ehuss_triagebot-test_milestones.json create mode 100644 tests/github_client/set_milestone/04-PATCH-repos_ehuss_triagebot-test_issues_115.json create mode 100644 tests/github_client/set_milestone/05-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/set_milestone/06-POST-repos_ehuss_triagebot-test_milestones.json create mode 100644 tests/github_client/set_milestone/07-GET-repos_ehuss_triagebot-test_milestones.json create mode 100644 tests/github_client/set_milestone/08-PATCH-repos_ehuss_triagebot-test_issues_115.json create mode 100644 tests/github_client/set_milestone/09-GET-repos_ehuss_triagebot-test_pulls_115.json create mode 100644 tests/github_client/submodule/00-GET-repos_rust-lang_rust.json create mode 100644 tests/github_client/submodule/01-GET-repos_rust-lang_rust_contents_src_doc_reference.json create mode 100644 tests/github_client/submodule/02-GET-repos_rust-lang_reference.json create mode 100644 tests/github_client/update_reference/00-GET-repos_ehuss_rust.json create mode 100644 tests/github_client/update_reference/01-PATCH-repos_ehuss_rust_git_refs_heads_docs-update.json create mode 100644 tests/github_client/update_tree/00-GET-repos_ehuss_rust.json create mode 100644 tests/github_client/update_tree/01-POST-repos_ehuss_rust_git_trees.json create mode 100644 tests/github_client/user/00-GET-user.json create mode 100644 tests/testsuite.rs diff --git a/README.md b/README.md index 85216d9a..147407a5 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,21 @@ You need to sign up for a free account, and also deal with configuring the GitHu * Secret: Enter a shared secret (some longish random text) * Events: "Send me everything" +## Tests + +When possible, writing unittests is very helpful and one of the easiest ways to test. +For more advanced testing, there is an integration test called `testsuite` which provides a testing environment for testing triagebot. +At this time, there is one part to it: + +* [`github_client`](tests/github_client/mod.rs) — Tests specifically targeting `GithubClient`. + This sets up an HTTP server that mimics api.github.com and verifies the client's behavior. + +Other parts may be added in the future, such as testing the database or the triagebot server itself. + +The real GitHub API responses are recorded in JSON files that the tests can later replay to verify the behavior of triagebot. +These recordings are enabled with the `TRIAGEBOT_TEST_RECORD_DIR` environment variable. +See the documentation in `github_client` for the steps for setting up recording to write a test. + ## License Triagebot is distributed under the terms of both the MIT license and the diff --git a/src/github.rs b/src/github.rs index 1fb3184a..735616c4 100644 --- a/src/github.rs +++ b/src/github.rs @@ -1,3 +1,4 @@ +use crate::test_record; use anyhow::{anyhow, Context}; use async_trait::async_trait; use bytes::Bytes; @@ -30,15 +31,18 @@ impl GithubClient { .build() .with_context(|| format!("building reqwest {}", req_dbg))?; + let test_capture_info = test_record::capture_request(&req); let mut resp = self.client.execute(req.try_clone().unwrap()).await?; if let Some(sleep) = Self::needs_retry(&resp).await { resp = self.retry(req, sleep, MAX_ATTEMPTS).await?; } + let status = resp.status(); let maybe_err = resp.error_for_status_ref().err(); let body = resp .bytes() .await .with_context(|| format!("failed to read response body {req_dbg}"))?; + test_record::record_request(test_capture_info, status, &body); if let Some(e) = maybe_err { return Err(anyhow::Error::new(e)) .with_context(|| format!("response: {}", String::from_utf8_lossy(&body))); @@ -248,7 +252,7 @@ pub struct Issue { pub number: u64, #[serde(deserialize_with = "opt_string")] pub body: String, - created_at: chrono::DateTime, + pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, /// The SHA for a merge commit. /// @@ -264,6 +268,7 @@ pub struct Issue { pub html_url: String, pub user: User, pub labels: Vec