Files
steep/src/gitea.rs
T
2026-06-02 19:52:50 +00:00

253 lines
6.5 KiB
Rust

use serde::Deserialize;
use serde_json::Value;
use crate::errors::AppError;
pub struct GiteaAPI {
base_url: String,
}
impl GiteaAPI {
pub fn new(base_url: &str) -> Self {
Self {
base_url: String::from(base_url),
}
}
pub async fn comment(&self, full_name: &str, index: u64) -> anyhow::Result<u64> {
Ok(1)
}
pub async fn edit_comment(&self, full_name: &str, id: u64) -> anyhow::Result<()> {
Ok(())
}
}
#[derive(Debug)]
pub enum WebhookType {
Review(ReviewPayload),
}
#[derive(Deserialize, Debug)]
pub struct ReviewPayload {
pub action: String,
pub pull_request: PullRequest,
pub repository: Repository,
pub comment: Comment,
}
#[derive(Deserialize, Debug)]
pub struct PullRequest {
pub id: u64,
pub diff_url: String,
}
#[derive(Deserialize, Debug)]
pub struct Comment {
pub id: u64,
pub body: String,
pub user: User,
}
#[derive(Deserialize, Debug)]
pub struct User {
pub id: u64,
}
#[derive(Deserialize, Debug)]
pub struct Repository {
pub full_name: String,
}
impl WebhookType {
pub fn from_event(event: &str, bot_name: &str, json: Value) -> Result<Self, AppError> {
let wb = match event {
"pull_request_comment" => Ok(WebhookType::Review(serde_json::from_value(json)?)),
_ => Err(AppError::UnknownEventErr),
}?;
let pr_body = match &wb {
WebhookType::Review(review_payload) => &review_payload.comment.body,
};
if !pr_body.starts_with(&format!("@{}", bot_name)) {
return Err(AppError::UnauthorizedUserErr);
}
let action = match &wb {
WebhookType::Review(review_payload) => &review_payload.action,
};
if action != "created" {
return Err(AppError::InvalidActionErr);
}
Ok(wb)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_from_event_valid_pull_request_comment() {
let json = json!({
"action": "created",
"pull_request": {
"id": 42,
"diff_url": "https://mydiff.fr"
},
"comment": {
"id": 7,
"body": "@test_bot LGTM",
"user": {
"id": 100
}
}
});
let result = WebhookType::from_event("pull_request_comment", "test_bot", json);
assert!(result.is_ok());
match result.unwrap() {
WebhookType::Review(payload) => {
assert_eq!(payload.action, "created");
assert_eq!(payload.pull_request.id, 42);
assert_eq!(payload.comment.id, 7);
assert_eq!(payload.comment.body, "@test_bot LGTM");
assert_eq!(payload.comment.user.id, 100);
}
}
}
#[test]
fn test_from_event_unknown_event() {
let json = json!({});
let result = WebhookType::from_event("push", "test_bot", json);
assert!(result.is_err());
match result.unwrap_err() {
AppError::UnknownEventErr => {}
_ => panic!("expected UnknownEventErr"),
}
}
#[test]
fn test_from_event_malformed_json() {
let json = json!({
"action": "created"
// pull_request and comment are missing
});
let result = WebhookType::from_event("pull_request_comment", "test_bot", json);
assert!(result.is_err());
match result.unwrap_err() {
AppError::BadJsonStructErr(_) => {}
_ => panic!("expected BadJsonStructErr"),
}
}
#[test]
fn test_from_event_rejects_non_created_action() {
let json = json!({
"action": "edited",
"pull_request": {
"id": 1,
"diff_url": "https://mydiff.fr"
},
"comment": {
"id": 1,
"body": "@test_bot body",
"user": {
"id": 1
}
}
});
let result = WebhookType::from_event("pull_request_comment", "test_bot", json);
assert!(result.is_err());
match result.unwrap_err() {
AppError::InvalidActionErr => {}
_ => panic!("expected InvalidActionErr"),
}
}
#[test]
fn test_deserialize_review_payload() {
let json = json!({
"action": "created",
"pull_request": {
"id": 99,
"diff_url": "https://mydiff.fr"
},
"comment": {
"id": 12,
"body": "Needs work",
"user": {
"id": 200
}
}
});
let payload: ReviewPayload = serde_json::from_value(json).unwrap();
assert_eq!(payload.action, "created");
assert_eq!(payload.pull_request.id, 99);
assert_eq!(payload.comment.id, 12);
assert_eq!(payload.comment.body, "Needs work");
assert_eq!(payload.comment.user.id, 200);
}
#[test]
fn test_from_event_empty_json() {
let result = WebhookType::from_event("pull_request_comment", "test_bot", json!({}));
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), AppError::BadJsonStructErr(_)));
}
#[test]
fn test_from_event_rejects_wrong_bot_name() {
let json = json!({
"action": "created",
"pull_request": {
"id": 1,
"diff_url": "https://mydiff.fr"
},
"comment": {
"id": 1,
"body": "@other_bot do something",
"user": {
"id": 1
}
}
});
let result = WebhookType::from_event("pull_request_comment", "test_bot", json);
assert!(matches!(result.unwrap_err(), AppError::UnauthorizedUserErr));
}
#[test]
fn test_from_event_rejects_no_bot_prefix() {
let json = json!({
"action": "created",
"pull_request": {
"id": 1,
"diff_url": "https://mydiff.fr"
},
"comment": {
"id": 1,
"body": "just a comment without bot mention",
"user": {
"id": 1
}
}
});
let result = WebhookType::from_event("pull_request_comment", "test_bot", json);
assert!(matches!(result.unwrap_err(), AppError::UnauthorizedUserErr));
}
}