Skip to content

Commit 113ccf2

Browse files
committed
testing: improve mocking ability for making Github API calls
1 parent a0853d3 commit 113ccf2

File tree

29 files changed

+513
-296
lines changed

29 files changed

+513
-296
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/src/controllers/api/app_installs.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::hookup_endpoint::hookup_post_authenticated;
55
use axum::Router;
66
use diesel::prelude::*;
77
use diesel::{QueryDsl, RunQueryDsl};
8-
use github_api::github_api_trait::{GithubApi, GithubApiTrait};
8+
use shared::github_api_trait::{GithubApi, GithubApiTrait};
99
use http::StatusCode;
1010
use shared::endpoints::defns::api::app_installs::create::{
1111
CreateAppInstallEndpoint, CreateAppInstallPayload, CreateAppInstallResponse,
@@ -36,11 +36,12 @@ pub fn create(router: Router<AppState>) -> Router<AppState> {
3636
.interact(|c| stmt.first::<UserId>(c))
3737
.await??;
3838

39-
GithubApi::apps_slash_get_installation(
40-
&state.config.get_gh_api_conf_with_app_auth(),
41-
*installation_id as i32,
42-
)
43-
.await?;
39+
GithubApi {}
40+
.apps_slash_get_installation(
41+
&state.config.get_gh_api_conf_with_app_auth(),
42+
*installation_id as i32,
43+
)
44+
.await?;
4445

4546
insert_installation_if_not_exists(
4647
&state,
@@ -58,4 +59,4 @@ pub fn create(router: Router<AppState>) -> Router<AppState> {
5859
))
5960
},
6061
)
61-
}
62+
}

backend/src/controllers/api/auth.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ use crate::{
1111
utils::gen_rand_string,
1212
};
1313
use axum::{extract::State, response::Redirect, routing::get, Router};
14-
use github_api::github_api_trait::{GithubApi, GithubApiTrait};
1514
use http::StatusCode;
1615
use shared::{
1716
consts::HEIMISCH_FRONTEND_DOMAIN,
1817
endpoints::defns::api::auth::{
1918
finish::{AuthFinishEndpoint, AuthFinishPayload, AuthFinishResponse},
2019
initiate::AuthInitiateEndpoint,
2120
},
21+
github_api_trait::{GithubApi, GithubApiTrait},
2222
types::user::UserId,
2323
};
2424

@@ -53,12 +53,13 @@ pub fn finish(router: Router<AppState>) -> Router<AppState> {
5353
)
5454
.await?;
5555

56-
let resp = GithubApi::users_slash_get_authenticated(
57-
&state
58-
.config
59-
.get_gh_api_conf_with_access_token(access_token.deref().clone()),
60-
)
61-
.await?;
56+
let resp = GithubApi {}
57+
.users_slash_get_authenticated(
58+
&state
59+
.config
60+
.get_gh_api_conf_with_access_token(access_token.deref().clone()),
61+
)
62+
.await?;
6263

6364
let (id, login, email) = match resp {
6465
github_api::models::UsersGetAuthenticated200Response::Private(private_user) => {

backend/src/tests/mod.rs

Lines changed: 122 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ use diesel_test::{
2828
DieselTestConfig,
2929
};
3030
use endpoint_test_client::PostEndpointTestClient;
31-
use github_api::github_api_trait::{GithubApi, GithubApiTrait};
3231
use github_api::models::{PrivateUser, UsersGetAuthenticated200Response};
3332
use github_webhook_body::WebhookBody;
3433
use http::StatusCode;
3534
use parking_lot::Mutex;
3635
use parse_request::ParsedHttpRequest;
3736
use serde_json::Value;
37+
use shared::github_api_trait::GithubApi;
3838
use shared::{
3939
endpoints::defns::api::{
40-
auth::{
41-
finish::{AuthFinishEndpoint, AuthFinishPayload, GithubAccessToken},
42-
initiate::AuthInitiateEndpoint,
43-
},
44-
websocket_updates::{ServerMsg, WEBSOCKET_UPDATES_ENDPOINT},
40+
auth::{
41+
finish::{AuthFinishEndpoint, AuthFinishPayload, GithubAccessToken},
42+
initiate::AuthInitiateEndpoint,
4543
},
44+
websocket_updates::{ServerMsg, WEBSOCKET_UPDATES_ENDPOINT},
45+
},
4646
types::{installation::InstallationId, user::UserId},
4747
};
4848
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
@@ -184,78 +184,80 @@ async fn with_user<Fut: Future>(
184184
.await?
185185
}
186186

187-
async fn with_logged_in_user<Fut: Future>(
188-
func: impl FnOnce(TestSetup, db::LoginUser) -> Fut,
189-
) -> TestResult<Fut::Output> {
190-
with_test_server(|test_setup| async {
191-
let TestSetup {
192-
pool: _,
193-
server,
194-
config,
195-
github_api_mock_server,
196-
github_non_api_mock_server,
197-
} = &test_setup;
198-
let initiate_response = server.get(AuthInitiateEndpoint::PATH).save_cookies().await;
199-
200-
let redirect_url: Url = initiate_response
201-
.headers()
202-
.get(http::header::LOCATION)
203-
.expect("")
204-
.to_str()
205-
.expect("")
206-
.parse()
207-
.unwrap();
208-
209-
let query_params = redirect_url
210-
.query_pairs()
211-
.map(|(k, v)| (k.to_lowercase(), v.into_owned()))
212-
.collect::<HashMap<_, _>>();
213-
214-
let state = query_params.get("state").unwrap().clone();
215-
let code = gen_rand_string(10);
216-
let access_token = GithubAccessToken::from(gen_rand_string(10));
217-
218-
get_user_access_token_request(
219-
&config.github_api.non_api_root,
220-
&code,
221-
&config.github_api.client_id,
222-
&config.github_api.client_secret,
223-
)
224-
.respond_with(ResponseTemplate::new(200).set_body_json(ATResp {
225-
access_token: access_token.clone(),
226-
}))
227-
.unwrap()
228-
.mount(github_non_api_mock_server)
229-
.await;
230-
231-
let user_id = UserId::from(rand::random::<i64>());
232-
let private_user = PrivateUser {
233-
id: *user_id.as_ref(),
234-
..Default::default()
235-
};
236-
GithubApi::users_slash_get_authenticated_request(
237-
&config.get_gh_api_conf_with_access_token(Some(access_token.as_str().to_owned())),
238-
)
239-
.respond_with(ResponseTemplate::new(200).set_body_json(
240-
UsersGetAuthenticated200Response::Private(Box::new(private_user)),
241-
))
242-
.unwrap()
243-
.mount(github_api_mock_server)
244-
.await;
245-
246-
let _finish_response =
247-
AuthFinishEndpoint::make_test_request(server, &AuthFinishPayload { state, code }, ())
248-
.await;
249-
250-
let user = get_login_user(&test_setup, &user_id)
251-
.await
252-
.expect("Running db query in test db failed")
253-
.expect("Test setup failed. User not found in db.");
254-
255-
func(test_setup, user).await
256-
})
257-
.await
258-
}
187+
// TODO: Rework to use the new GihtubApiTrait instead of doing wiremock things.
188+
//async fn with_logged_in_user<Fut: Future>(
189+
// func: impl FnOnce(TestSetup, db::LoginUser) -> Fut,
190+
//) -> TestResult<Fut::Output> {
191+
// with_test_server(|test_setup| async {
192+
// let TestSetup {
193+
// pool: _,
194+
// server,
195+
// config,
196+
// github_api_mock_server,
197+
// github_non_api_mock_server,
198+
// } = &test_setup;
199+
// let initiate_response = server.get(AuthInitiateEndpoint::PATH).save_cookies().await;
200+
//
201+
// let redirect_url: Url = initiate_response
202+
// .headers()
203+
// .get(http::header::LOCATION)
204+
// .expect("")
205+
// .to_str()
206+
// .expect("")
207+
// .parse()
208+
// .unwrap();
209+
//
210+
// let query_params = redirect_url
211+
// .query_pairs()
212+
// .map(|(k, v)| (k.to_lowercase(), v.into_owned()))
213+
// .collect::<HashMap<_, _>>();
214+
//
215+
// let state = query_params.get("state").unwrap().clone();
216+
// let code = gen_rand_string(10);
217+
// let access_token = GithubAccessToken::from(gen_rand_string(10));
218+
//
219+
// get_user_access_token_request(
220+
// &config.github_api.non_api_root,
221+
// &code,
222+
// &config.github_api.client_id,
223+
// &config.github_api.client_secret,
224+
// )
225+
// .respond_with(ResponseTemplate::new(200).set_body_json(ATResp {
226+
// access_token: access_token.clone(),
227+
// }))
228+
// .unwrap()
229+
// .mount(github_non_api_mock_server)
230+
// .await;
231+
//
232+
// let user_id = UserId::from(rand::random::<i64>());
233+
// let private_user = PrivateUser {
234+
// id: *user_id.as_ref(),
235+
// ..Default::default()
236+
// };
237+
// GithubApi {}
238+
// .users_slash_get_authenticated_request(
239+
// &config.get_gh_api_conf_with_access_token(Some(access_token.as_str().to_owned())),
240+
// )
241+
// .respond_with(ResponseTemplate::new(200).set_body_json(
242+
// UsersGetAuthenticated200Response::Private(Box::new(private_user)),
243+
// ))
244+
// .unwrap()
245+
// .mount(github_api_mock_server)
246+
// .await;
247+
//
248+
// let _finish_response =
249+
// AuthFinishEndpoint::make_test_request(server, &AuthFinishPayload { state, code }, ())
250+
// .await;
251+
//
252+
// let user = get_login_user(&test_setup, &user_id)
253+
// .await
254+
// .expect("Running db query in test db failed")
255+
// .expect("Test setup failed. User not found in db.");
256+
//
257+
// func(test_setup, user).await
258+
// })
259+
// .await
260+
//}
259261

260262
async fn deliver_issue_comment_webhook_fixture(
261263
test_setup: &TestSetup,
@@ -331,44 +333,45 @@ async fn test_simple_webhook_delivery() -> TestResult<()> {
331333
.await?
332334
}
333335

334-
#[tokio::test]
335-
async fn test_websocket_updates() -> TestResult<()> {
336-
with_logged_in_user(|test_setup, user| async move {
337-
let mut ws_request = test_setup
338-
.server
339-
.get_websocket(WEBSOCKET_UPDATES_ENDPOINT)
340-
.save_cookies()
341-
.await
342-
.into_websocket()
343-
.await;
344-
345-
ws_request.send_message(WsMessage::Ping(vec![])).await;
346-
match ws_request.receive_message().await {
347-
WsMessage::Pong(_) => (),
348-
a => panic!("Unexpecteed message: {a:?}"),
349-
};
350-
351-
tokio::time::sleep(Duration::from_secs(2)).await;
352-
353-
let (_, parsed_webhook_request, _) =
354-
deliver_issue_comment_webhook_fixture(&test_setup, user.github_user_id).await?;
355-
356-
let server_msg = tokio::time::timeout(
357-
Duration::from_secs(2),
358-
ws_request.receive_json::<ServerMsg>(),
359-
)
360-
.await
361-
.expect("Expected too long to receive a message on the websocket.");
362-
363-
let expected_webhook_body =
364-
serde_json::from_slice::<WebhookBody>(&parsed_webhook_request.body).expect("");
365-
assert_eq!(
366-
serde_json::to_value(server_msg.body).unwrap(),
367-
serde_json::to_value(expected_webhook_body).unwrap()
368-
);
369-
370-
ws_request.close().await;
371-
Ok(())
372-
})
373-
.await?
374-
}
336+
// NOTE: Waiting for with_logged_in_user to be reworked.
337+
//#[tokio::test]
338+
//async fn test_websocket_updates() -> TestResult<()> {
339+
// with_logged_in_user(|test_setup, user| async move {
340+
// let mut ws_request = test_setup
341+
// .server
342+
// .get_websocket(WEBSOCKET_UPDATES_ENDPOINT)
343+
// .save_cookies()
344+
// .await
345+
// .into_websocket()
346+
// .await;
347+
//
348+
// ws_request.send_message(WsMessage::Ping(vec![])).await;
349+
// match ws_request.receive_message().await {
350+
// WsMessage::Pong(_) => (),
351+
// a => panic!("Unexpecteed message: {a:?}"),
352+
// };
353+
//
354+
// tokio::time::sleep(Duration::from_secs(2)).await;
355+
//
356+
// let (_, parsed_webhook_request, _) =
357+
// deliver_issue_comment_webhook_fixture(&test_setup, user.github_user_id).await?;
358+
//
359+
// let server_msg = tokio::time::timeout(
360+
// Duration::from_secs(2),
361+
// ws_request.receive_json::<ServerMsg>(),
362+
// )
363+
// .await
364+
// .expect("Expected too long to receive a message on the websocket.");
365+
//
366+
// let expected_webhook_body =
367+
// serde_json::from_slice::<WebhookBody>(&parsed_webhook_request.body).expect("");
368+
// assert_eq!(
369+
// serde_json::to_value(server_msg.body).unwrap(),
370+
// serde_json::to_value(expected_webhook_body).unwrap()
371+
// );
372+
//
373+
// ws_request.close().await;
374+
// Ok(())
375+
// })
376+
// .await?
377+
//}

github_api/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ reqwest = { version = "0.12", features = ["json", "multipart"] }
1616
jiff = { version = "*", default-features = false, features = ["serde"] }
1717
reqwest_wiremock = { path = "../reqwest_wiremock" }
1818
serde_path_to_error = "0.1.16"
19+
bon = "3.3.2"
1920

2021
[features]
21-
ssr = ["reqwest_wiremock/mocking"]
22+
ssr = ["reqwest_wiremock/mocking"]

github_api/src/apis/issues_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ pub enum IssuesSlashListForRepoError {
570570
// }
571571
//
572572
/// Any user with pull access to a repository can create an issue. If [issues are disabled in the repository](https://docs.github.com/articles/disabling-issues/), the API returns a `410 Gone` status. This endpoint triggers [notifications](https://docs.github.com/github/managing-subscriptions-and-notifications-on-github/about-notifications). Creating content too quickly using this endpoint may result in secondary rate limiting. For more information, see \"[Rate limits for the API](https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api#about-secondary-rate-limits)\" and \"[Best practices for using the REST API](https://docs.github.com/rest/guides/best-practices-for-using-the-rest-api).\" This endpoint supports the following custom media types. For more information, see \"[Media types](https://docs.github.com/rest/using-the-rest-api/getting-started-with-the-rest-api#media-types).\" - **`application/vnd.github.raw+json`**: Returns the raw markdown body. Response will include `body`. This is the default if you do not pass any specific media type. - **`application/vnd.github.text+json`**: Returns a text only representation of the markdown body. Response will include `body_text`. - **`application/vnd.github.html+json`**: Returns HTML rendered from the body's markdown. Response will include `body_html`. - **`application/vnd.github.full+json`**: Returns raw, text, and HTML representations. Response will include `body`, `body_text`, and `body_html`.
573-
pub(crate) async fn issues_slash_create(
573+
pub async fn issues_slash_create(
574574
configuration: &configuration::Configuration,
575575
owner: &str,
576576
repo: &str,

github_api/src/apis/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String
9494

9595
// pub mod actions_api;
9696
// pub mod activity_api;
97-
pub(crate) mod apps_api;
97+
pub mod apps_api;
9898
// pub mod billing_api;
9999
// pub mod checks_api;
100100
// pub mod classroom_api;
@@ -110,7 +110,7 @@ pub(crate) mod apps_api;
110110
// pub mod git_api;
111111
// pub mod gitignore_api;
112112
// pub mod interactions_api;
113-
pub(crate) mod issues_api;
113+
pub mod issues_api;
114114
// pub mod licenses_api;
115115
// pub mod markdown_api;
116116
// pub mod meta_api;
@@ -127,6 +127,6 @@ pub(crate) mod issues_api;
127127
// pub mod secret_scanning_api;
128128
// pub mod security_advisories_api;
129129
// pub mod teams_api;
130-
pub(crate) mod users_api;
130+
pub mod users_api;
131131

132132
pub mod configuration;

github_api/src/apis/users_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ pub enum UsersSlashGetAuthenticatedError {
11251125
// }
11261126
//
11271127
/// OAuth app tokens and personal access tokens (classic) need the `user` scope in order for the response to include private profile information.
1128-
pub(crate) async fn users_slash_get_authenticated(
1128+
pub async fn users_slash_get_authenticated(
11291129
configuration: &configuration::Configuration,
11301130
) -> Result<models::UsersGetAuthenticated200Response, Error<UsersSlashGetAuthenticatedError>> {
11311131
let local_var_resp = users_slash_get_authenticated_request(configuration)

0 commit comments

Comments
 (0)