mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
228 lines
6 KiB
Rust
228 lines
6 KiB
Rust
use fabro_github::{
|
|
GitHubAppCredentials, GitHubContext, GitHubCredentials, close_pull_request,
|
|
create_installation_access_token_for_pr, create_pull_request, enable_auto_merge,
|
|
get_pull_request, merge_pull_request, resolve_authenticated_url, sign_app_jwt,
|
|
};
|
|
use fabro_test::{GitHubAppOptions, GitHubAppState, TwinGitHub};
|
|
use fabro_types::settings::run::MergeStrategy;
|
|
|
|
const TEST_RSA_KEY: &str = include_str!("../src/testdata/rsa_private.pem");
|
|
|
|
fn github_credentials() -> GitHubCredentials {
|
|
GitHubCredentials::App(GitHubAppCredentials {
|
|
app_id: "42".to_string(),
|
|
private_key_pem: TEST_RSA_KEY.to_string(),
|
|
slug: Some("test-app".to_string()),
|
|
})
|
|
}
|
|
|
|
fn standard_app_state() -> GitHubAppState {
|
|
let mut state = GitHubAppState::new();
|
|
state.register_app(GitHubAppOptions {
|
|
app_id: "42".into(),
|
|
slug: "test-app".into(),
|
|
owner_login: "acme".into(),
|
|
public: true,
|
|
private_key_pem: TEST_RSA_KEY.into(),
|
|
webhook_secret: None,
|
|
});
|
|
state.add_installation("42", "acme", vec!["widgets".into()], false);
|
|
state.add_repository(
|
|
"acme",
|
|
"widgets",
|
|
vec!["main".into(), "feature".into()],
|
|
false,
|
|
);
|
|
state
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn create_and_get_pull_request() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
let ctx = &GitHubContext::new(&creds, &twin.base_url);
|
|
|
|
let created = create_pull_request(
|
|
ctx,
|
|
"acme",
|
|
"widgets",
|
|
"main",
|
|
"feature",
|
|
"Add widgets",
|
|
"PR body",
|
|
false,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pr = get_pull_request(ctx, "acme", "widgets", created.number)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(pr.title, "Add widgets");
|
|
assert_eq!(pr.state, "open");
|
|
assert_eq!(pr.head.ref_name, "feature");
|
|
assert_eq!(pr.base.ref_name, "main");
|
|
|
|
twin.shutdown().await;
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn create_merge_and_verify_state() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
let ctx = &GitHubContext::new(&creds, &twin.base_url);
|
|
|
|
let created = create_pull_request(
|
|
ctx, "acme", "widgets", "main", "feature", "Merge me", "PR body", false,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
merge_pull_request(
|
|
ctx,
|
|
"acme",
|
|
"widgets",
|
|
created.number,
|
|
MergeStrategy::Squash,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pr = get_pull_request(ctx, "acme", "widgets", created.number)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(pr.state, "closed");
|
|
assert_eq!(pr.mergeable, Some(false));
|
|
|
|
twin.shutdown().await;
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn create_close_and_verify_state() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
|
|
let ctx = &GitHubContext::new(&creds, &twin.base_url);
|
|
|
|
let created = create_pull_request(
|
|
ctx, "acme", "widgets", "main", "feature", "Close me", "PR body", false,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
close_pull_request(ctx, "acme", "widgets", created.number)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pr = get_pull_request(ctx, "acme", "widgets", created.number)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(pr.state, "closed");
|
|
|
|
twin.shutdown().await;
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn enable_auto_merge_persists() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
|
|
let ctx = &GitHubContext::new(&creds, &twin.base_url);
|
|
|
|
let created = create_pull_request(
|
|
ctx,
|
|
"acme",
|
|
"widgets",
|
|
"main",
|
|
"feature",
|
|
"Auto merge me",
|
|
"PR body",
|
|
false,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
enable_auto_merge(
|
|
ctx,
|
|
"acme",
|
|
"widgets",
|
|
&created.node_id,
|
|
MergeStrategy::Squash,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let GitHubCredentials::App(app_creds) = &creds else {
|
|
panic!("expected app credentials");
|
|
};
|
|
let jwt = sign_app_jwt(&app_creds.app_id, &app_creds.private_key_pem).unwrap();
|
|
let client = fabro_test::test_http_client();
|
|
let token =
|
|
create_installation_access_token_for_pr(&client, &jwt, "acme", "widgets", &twin.base_url)
|
|
.await
|
|
.unwrap();
|
|
|
|
let detail: serde_json::Value = fabro_test::test_http_client()
|
|
.get(format!(
|
|
"{}/repos/acme/widgets/pulls/{}",
|
|
twin.base_url, created.number
|
|
))
|
|
.header("Authorization", format!("Bearer {token}"))
|
|
.header("Accept", "application/vnd.github+json")
|
|
.header("User-Agent", "fabro")
|
|
.send()
|
|
.await
|
|
.unwrap()
|
|
.json()
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
detail["auto_merge"]["merge_method"].as_str(),
|
|
Some("SQUASH")
|
|
);
|
|
|
|
twin.shutdown().await;
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn resolve_authenticated_url_embeds_token() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
|
|
let url = resolve_authenticated_url(
|
|
&GitHubContext::new(&creds, &twin.base_url),
|
|
"https://github.com/acme/widgets.git",
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(url.raw_string().starts_with("https://x-access-token:ghs_"));
|
|
assert!(url.raw_string().contains("github.com/acme/widgets.git"));
|
|
assert!(
|
|
url.redacted_string()
|
|
.starts_with("https://x-access-token:****@")
|
|
);
|
|
|
|
twin.shutdown().await;
|
|
}
|
|
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn resolve_authenticated_url_errors_on_non_github_url() {
|
|
let twin = TwinGitHub::start(standard_app_state()).await;
|
|
let creds = github_credentials();
|
|
|
|
let error = resolve_authenticated_url(
|
|
&GitHubContext::new(&creds, &twin.base_url),
|
|
"https://gitlab.com/foo/bar",
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
|
|
assert!(error.to_string().contains("Not a GitHub HTTPS URL"));
|
|
|
|
twin.shutdown().await;
|
|
}
|