mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-04 02:33:56 +00:00
test(twin-github): stop generating rsa keys during tests
This commit is contained in:
parent
1d7ef425ab
commit
1b49558f49
12 changed files with 138 additions and 315 deletions
|
|
@ -116,59 +116,22 @@ pub fn ensure_permission(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::state::derive_public_key_pem;
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key, test_rsa_public_key};
|
||||
|
||||
#[test]
|
||||
fn verify_valid_jwt() {
|
||||
let private_pem = test_rsa_key();
|
||||
let public_pem = derive_public_key_pem(&private_pem);
|
||||
let jwt = sign_test_jwt("12345", &private_pem);
|
||||
let result = verify_app_jwt(&jwt, &public_pem);
|
||||
let private_pem = test_rsa_private_key();
|
||||
let jwt = sign_test_jwt("12345", private_pem);
|
||||
let result = verify_app_jwt(&jwt, test_rsa_public_key());
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), "12345");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_invalid_jwt() {
|
||||
let private_pem = test_rsa_key();
|
||||
let public_pem = derive_public_key_pem(&private_pem);
|
||||
let result = verify_app_jwt("invalid.jwt.token", &public_pem);
|
||||
let derived_public_pem = derive_public_key_pem(test_rsa_private_key());
|
||||
assert_eq!(derived_public_pem, test_rsa_public_key());
|
||||
let result = verify_app_jwt("invalid.jwt.token", test_rsa_public_key());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,21 +372,8 @@ fn next_pr_number(pull_requests: &HashMap<(String, String), Vec<PullRequest>>) -
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
fn test_rsa_key() -> &'static str {
|
||||
crate::test_support::test_rsa_private_key()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -548,7 +535,7 @@ impl FixtureState {
|
|||
slug: "fixture-app".to_string(),
|
||||
owner_login: "acme".to_string(),
|
||||
public: true,
|
||||
private_key_pem: test_rsa_key(),
|
||||
private_key_pem: test_rsa_key().to_string(),
|
||||
webhook_secret: Some("whsec".to_string()),
|
||||
}],
|
||||
..Self::default()
|
||||
|
|
|
|||
|
|
@ -113,59 +113,23 @@ pub async fn patch_webhook_config(
|
|||
mod tests {
|
||||
use crate::server::TestServer;
|
||||
use crate::state::{AppOptions, AppState};
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key};
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_app_returns_app_info() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "12345".to_string(),
|
||||
slug: "my-app".to_string(),
|
||||
owner_login: "my-org".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("12345", &pem);
|
||||
let jwt = sign_test_jwt("12345", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.get(&format!("{}/app", server.url()))
|
||||
|
|
@ -185,14 +149,14 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_app_rejects_invalid_jwt() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "12345".to_string(),
|
||||
slug: "my-app".to_string(),
|
||||
owner_login: "my-org".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem,
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let server = TestServer::start(state).await;
|
||||
|
|
@ -211,14 +175,14 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_apps_slug_public() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "12345".to_string(),
|
||||
slug: "my-app".to_string(),
|
||||
owner_login: "my-org".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem,
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let server = TestServer::start(state).await;
|
||||
|
|
@ -238,14 +202,14 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_apps_slug_private_returns_404() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "12345".to_string(),
|
||||
slug: "private-app".to_string(),
|
||||
owner_login: "my-org".to_string(),
|
||||
public: false,
|
||||
private_key_pem: pem,
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let server = TestServer::start(state).await;
|
||||
|
|
|
|||
|
|
@ -93,43 +93,7 @@ pub async fn get_branch(
|
|||
mod tests {
|
||||
use crate::server::TestServer;
|
||||
use crate::state::{AppOptions, AppState};
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key};
|
||||
|
||||
async fn get_installation_token(
|
||||
client: &reqwest::Client,
|
||||
|
|
@ -167,14 +131,14 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn branch_exists_returns_200() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
state.add_installation("100", "owner", vec!["repo".to_string()], false);
|
||||
|
|
@ -186,7 +150,7 @@ mod tests {
|
|||
);
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let token = get_installation_token(&client, &jwt, "owner", "repo", server.url()).await;
|
||||
|
||||
|
|
@ -210,21 +174,21 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn branch_not_found_returns_404() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
state.add_installation("100", "owner", vec!["repo".to_string()], false);
|
||||
state.add_repository("owner", "repo", vec!["main".to_string()], false);
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let token = get_installation_token(&client, &jwt, "owner", "repo", server.url()).await;
|
||||
|
||||
|
|
|
|||
|
|
@ -614,43 +614,7 @@ fn extract_unquoted_value(s: &str, key: &str) -> Option<String> {
|
|||
mod tests {
|
||||
use crate::server::TestServer;
|
||||
use crate::state::{AppOptions, AppState, PullRequest};
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key};
|
||||
|
||||
async fn get_installation_token(
|
||||
client: &reqwest::Client,
|
||||
|
|
@ -718,10 +682,10 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn viewer_query_returns_id() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.viewer_id = "U_testviewer".to_string();
|
||||
let (server, client, token) = setup_with_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_with_token(&mut state, pem).await;
|
||||
|
||||
let resp = client
|
||||
.post(&format!("{}/graphql", server.url()))
|
||||
|
|
@ -742,7 +706,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn enable_auto_merge_mutation() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
// Pre-seed a PR
|
||||
state
|
||||
|
|
@ -768,7 +732,7 @@ mod tests {
|
|||
updated_at: "2026-01-01T00:00:00Z".to_string(),
|
||||
auto_merge: None,
|
||||
});
|
||||
let (server, client, token) = setup_with_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_with_token(&mut state, pem).await;
|
||||
|
||||
let query = r#"mutation {
|
||||
enablePullRequestAutoMerge(input: {pullRequestId: "PR_test123", mergeMethod: SQUASH}) {
|
||||
|
|
@ -824,7 +788,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn org_project_query_returns_node_id() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.projects.push(crate::state::Project {
|
||||
node_id: "PVT_org123".to_string(),
|
||||
|
|
@ -862,7 +826,7 @@ mod tests {
|
|||
},
|
||||
}],
|
||||
});
|
||||
let (server, client, token) = setup_with_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_with_token(&mut state, pem).await;
|
||||
|
||||
// Query org project
|
||||
let query = r#"
|
||||
|
|
@ -894,7 +858,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn project_items_query_returns_paginated() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.projects.push(crate::state::Project {
|
||||
node_id: "PVT_test".to_string(),
|
||||
|
|
@ -919,7 +883,7 @@ mod tests {
|
|||
},
|
||||
}],
|
||||
});
|
||||
let (server, client, token) = setup_with_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_with_token(&mut state, pem).await;
|
||||
|
||||
let query = r#"
|
||||
query($projectId: ID!, $cursor: String) {
|
||||
|
|
@ -978,7 +942,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn update_project_item_field_value() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.projects.push(crate::state::Project {
|
||||
node_id: "PVT_test".to_string(),
|
||||
|
|
@ -1012,7 +976,7 @@ mod tests {
|
|||
},
|
||||
}],
|
||||
});
|
||||
let (server, client, token) = setup_with_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_with_token(&mut state, pem).await;
|
||||
|
||||
let query = r#"
|
||||
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
||||
|
|
|
|||
|
|
@ -178,61 +178,25 @@ pub async fn create_access_token(
|
|||
mod tests {
|
||||
use crate::server::TestServer;
|
||||
use crate::state::{AppOptions, AppState};
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key};
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_installation_returns_id() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
state.add_installation("100", "owner", vec!["repo".to_string()], false);
|
||||
state.add_repository("owner", "repo", vec!["main".to_string()], false);
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.get(&format!("{}/repos/owner/repo/installation", server.url()))
|
||||
|
|
@ -250,20 +214,20 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_installation_returns_404_when_not_installed() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
// No installation added
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.get(&format!("{}/repos/owner/repo/installation", server.url()))
|
||||
|
|
@ -278,20 +242,20 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_installation_returns_403_when_suspended() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
state.add_installation("100", "owner", vec!["repo".to_string()], true); // suspended
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.get(&format!("{}/repos/owner/repo/installation", server.url()))
|
||||
|
|
@ -306,20 +270,20 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn create_access_token_returns_201() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let install_id = state.add_installation("100", "owner", vec!["repo".to_string()], false);
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.post(&format!(
|
||||
|
|
@ -344,20 +308,20 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn create_access_token_returns_422_for_unauthorized_repo() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "100".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem.clone(),
|
||||
private_key_pem: pem.to_string(),
|
||||
webhook_secret: None,
|
||||
});
|
||||
let install_id = state.add_installation("100", "owner", vec!["repo".to_string()], false);
|
||||
let server = TestServer::start(state).await;
|
||||
|
||||
let jwt = sign_test_jwt("100", &pem);
|
||||
let jwt = sign_test_jwt("100", pem);
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.post(&format!(
|
||||
|
|
|
|||
|
|
@ -303,43 +303,7 @@ fn pr_to_json(pr: &PullRequest) -> serde_json::Value {
|
|||
mod tests {
|
||||
use crate::server::TestServer;
|
||||
use crate::state::{AppOptions, AppState};
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
use crate::test_support::{sign_test_jwt, test_rsa_private_key};
|
||||
|
||||
async fn get_installation_token(
|
||||
client: &reqwest::Client,
|
||||
|
|
@ -405,9 +369,9 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn create_pr_returns_201() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
let (server, client, token) = setup_and_get_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_and_get_token(&mut state, pem).await;
|
||||
|
||||
let resp = client
|
||||
.post(&format!("{}/repos/owner/repo/pulls", server.url()))
|
||||
|
|
@ -435,9 +399,9 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn get_pr_returns_detail() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
let (server, client, token) = setup_and_get_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_and_get_token(&mut state, pem).await;
|
||||
|
||||
// Create a PR first
|
||||
let create_resp = client
|
||||
|
|
@ -485,9 +449,9 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn merge_pr_returns_200() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
let (server, client, token) = setup_and_get_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_and_get_token(&mut state, pem).await;
|
||||
|
||||
// Create a PR
|
||||
let create_resp = client
|
||||
|
|
@ -530,9 +494,9 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn close_pr_returns_200() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
let (server, client, token) = setup_and_get_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_and_get_token(&mut state, pem).await;
|
||||
|
||||
// Create a PR
|
||||
let create_resp = client
|
||||
|
|
@ -562,9 +526,9 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn merge_nonexistent_pr_returns_404() {
|
||||
let pem = test_rsa_key();
|
||||
let pem = test_rsa_private_key();
|
||||
let mut state = AppState::new();
|
||||
let (server, client, token) = setup_and_get_token(&mut state, &pem).await;
|
||||
let (server, client, token) = setup_and_get_token(&mut state, pem).await;
|
||||
|
||||
let resp = client
|
||||
.put(&format!(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ pub mod fixtures;
|
|||
pub mod handlers;
|
||||
pub mod server;
|
||||
pub mod state;
|
||||
#[cfg(test)]
|
||||
pub mod test_support;
|
||||
|
||||
pub use server::TestServer;
|
||||
pub use state::AppState;
|
||||
|
|
|
|||
|
|
@ -429,6 +429,7 @@ pub fn init_git_repos(state: &mut AppState, git_root: &std::path::Path) -> Resul
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_support::{test_rsa_private_key, test_rsa_public_key};
|
||||
|
||||
#[test]
|
||||
fn empty_state_has_no_apps() {
|
||||
|
|
@ -436,22 +437,6 @@ mod tests {
|
|||
assert!(state.apps.is_empty());
|
||||
}
|
||||
|
||||
fn test_rsa_key() -> String {
|
||||
use std::process::Command;
|
||||
let output = Command::new("openssl")
|
||||
.args([
|
||||
"genpkey",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
])
|
||||
.output()
|
||||
.expect("openssl should be available");
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repository_has_private_field() {
|
||||
let mut state = AppState::new();
|
||||
|
|
@ -475,19 +460,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_register_app() {
|
||||
let pem = test_rsa_key();
|
||||
let mut state = AppState::new();
|
||||
state.register_app(AppOptions {
|
||||
app_id: "12345".to_string(),
|
||||
slug: "test-app".to_string(),
|
||||
owner_login: "test-owner".to_string(),
|
||||
public: true,
|
||||
private_key_pem: pem,
|
||||
private_key_pem: test_rsa_private_key().to_string(),
|
||||
webhook_secret: Some("secret".to_string()),
|
||||
});
|
||||
assert_eq!(state.apps.len(), 1);
|
||||
assert_eq!(state.apps["12345"].config.slug, "test-app");
|
||||
// Verify public key was derived
|
||||
assert!(state.apps["12345"].public_key_pem.contains("PUBLIC KEY"));
|
||||
assert_eq!(state.apps["12345"].public_key_pem, test_rsa_public_key());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
test/twin/github/src/test_support.rs
Normal file
31
test/twin/github/src/test_support.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#[cfg(test)]
|
||||
pub fn test_rsa_private_key() -> &'static str {
|
||||
include_str!("testdata/rsa_private.pem")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_rsa_public_key() -> &'static str {
|
||||
include_str!("testdata/rsa_public.pem")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn sign_test_jwt(app_id: &str, private_key_pem: &str) -> String {
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
iss: String,
|
||||
iat: i64,
|
||||
exp: i64,
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let claims = Claims {
|
||||
iss: app_id.to_string(),
|
||||
iat: now - 60,
|
||||
exp: now + 600,
|
||||
};
|
||||
let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap();
|
||||
encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap()
|
||||
}
|
||||
28
test/twin/github/src/testdata/rsa_private.pem
vendored
Normal file
28
test/twin/github/src/testdata/rsa_private.pem
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCty51n9z4E+fGI
|
||||
7m/AC2ZS5PoLgi3ZR8XyUwy10XdbVKQvCCJ04cUgml2TCqA5dFEFsj9a+Ooyz9hE
|
||||
J70g9U8iFBq53lx3Og3cvZUy1vdnwInMQhdXzmJAdkqZl4/aW7V/571yXfDPZBb5
|
||||
295Iof3FMEwWzYQMiQm7kZrshRirQBfvlvpIu0N30ZwmyC1MbH0ESBq86owLiVzf
|
||||
0IBO4dOxmvSTX12Qd1v7+N3hPqMyP0F4PQJM7uLRfHYIO8NVclpS+crj4GJMp9Cj
|
||||
r1BJL0GvRDZsWazzLg/PVt0sEFJMmY0HoSQCZ2ybqXiD46VXDVL/q0ioxje9bE8X
|
||||
ZGEUWM1ZAgMBAAECgf8PerJhVry3lzYbrvmXH6FRLRcRsPsSYNcRAYXdASih+C6G
|
||||
mNVYAAV8rwf4otCfObgEQakkkFKAN1uG3eJocS9HdacMjmC+XpmlrrMz1aYwOfRA
|
||||
hvE0sUxNU/o6BeuG1HMFkI+iwon6FjAWg+LdzQyGrKdies4BhFPyNI38AvkDM+BR
|
||||
0z8xOESh+GkMjRcArIxKnzlFaj+BOOza6Ju3cAZfK7Sw+YvxIu7ECB8Mf+0uEX5Y
|
||||
95AtnD7v3bA/kij1Ate3po9KWr7jfBBFwlNuqYFZ28Y9nSg+p69EMmZK9YQseFwo
|
||||
72t4+lM60J1MuK08khVvWHsHyZzbCquKKyoVlE0CgYEA19UUbCbwIi2KgJA+5YFF
|
||||
DWx5KQXo8t6tDa2jWcCorzm/y3r6jCf7Ja6zIjyZFGtqZw+5z6pD7/T2QUc/GIHO
|
||||
JXo6CDJwyrmcPYb0qmqunCApfpSdXy22Dk43sDcanLF0+cLYF13T25WiebNxswtC
|
||||
6knS8Nj2gIMdLGqwhgXxalUCgYEAziPDhKbh0GVTE09JLbZXvETuzOoPIQVSHq52
|
||||
0/eTxxp5DpNhERn/GGFkXYlg9XoYye+cvf7t3vhYUdefxl1qbnLp/aaq/pH4m4Zb
|
||||
sXeTH+fcyG8e+H1gCnKL7mLc733JcQ/PkT2VemWwE59nobqC8a4S3jSVLgb830J8
|
||||
Tj+z4vUCgYBlmRYt+v5a+FkUP9J7sqhQs1ktaGzJtux7XdthKoG9Sk/wdwlt5Hwi
|
||||
tuP4H9JnfFDwBsUO7jVgxU0OWX1oCeKCXdDnfub9ZvYXOGn/toxPTgx6W3bhEOF8
|
||||
RnGMb8QFMru/vmp+RVqvKPb8lu3UvF5ZbzruyRLwV8fp2qcRz8ZeqQKBgAvF8SG/
|
||||
YkBDuTXuHlDVDDe9fCIJ8ClDo4gLENBUYIVLlsRlWHLgmDgtP80zzmzWt1m5LKuR
|
||||
RsaoSmsKNJ7gsPZz67dH8JYvghJn3w7ItIdq96oe5pbsmjKeji1kShgHiBzD+Wfj
|
||||
K4ldC2xje0i6XUYuWfeE60xAq+qYYn8Zlk2NAoGACNwVYYdj7YkjfclzggBn08qV
|
||||
K8GAQQ/p6Cnsq1/2YHtugEeAtASazR8GqOzV6wy0nGi6XaH6Kw5cDZ7ACETocSX+
|
||||
wM30YkPJBg0WUsCzsLg6B1ri8gnJCUBp2IwMeSU+Av5GG09+oqEwawI5WoQO1+TD
|
||||
rUZ9gbbIAXA79BD11L4=
|
||||
-----END PRIVATE KEY-----
|
||||
9
test/twin/github/src/testdata/rsa_public.pem
vendored
Normal file
9
test/twin/github/src/testdata/rsa_public.pem
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArcudZ/c+BPnxiO5vwAtm
|
||||
UuT6C4It2UfF8lMMtdF3W1SkLwgidOHFIJpdkwqgOXRRBbI/WvjqMs/YRCe9IPVP
|
||||
IhQaud5cdzoN3L2VMtb3Z8CJzEIXV85iQHZKmZeP2lu1f+e9cl3wz2QW+dveSKH9
|
||||
xTBMFs2EDIkJu5Ga7IUYq0AX75b6SLtDd9GcJsgtTGx9BEgavOqMC4lc39CATuHT
|
||||
sZr0k19dkHdb+/jd4T6jMj9BeD0CTO7i0Xx2CDvDVXJaUvnK4+BiTKfQo69QSS9B
|
||||
r0Q2bFms8y4Pz1bdLBBSTJmNB6EkAmdsm6l4g+OlVw1S/6tIqMY3vWxPF2RhFFjN
|
||||
WQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
Loading…
Add table
Reference in a new issue