Skip to content

Commit 90e013e

Browse files
Merge pull request #67 from reddevilmidzy/test-url
링크 유효 검증 추가
2 parents 3bd3429 + a54d915 commit 90e013e

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

rook/src/link/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mod tests {
3939

4040
#[tokio::test]
4141
async fn validate_link() {
42-
let link = "https://redddy.com";
42+
let link = "https://redddy.ai";
4343
assert!(matches!(
4444
check_link(link).await,
4545
LinkCheckResult::Invalid(_)

rook/src/main.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use std::time::Duration;
1010
use tracing::{Level, error, info};
1111
use tracing_subscriber::FmtSubscriber;
1212

13+
const GITHUB_BASE_URL: &str = "https://github.com/";
14+
const GITHUB_URL_FORMAT: &str = "https://github.com/{owner}/{repo_name}";
15+
1316
async fn spawn_repository_checker(
1417
repo_url: &str,
1518
branch: Option<String>,
@@ -33,20 +36,20 @@ async fn health_check() -> &'static str {
3336

3437
#[derive(Deserialize)]
3538
struct CheckRequest {
36-
repo_url: String,
39+
repo: RepositoryURL,
3740
branch: Option<String>,
3841
interval_secs: Option<u64>,
3942
}
4043

4144
async fn check_handler(Json(payload): Json<CheckRequest>) -> Result<&'static str, StatusCode> {
4245
info!(
4346
"Received check request for repository: {}, branch: {:?}",
44-
payload.repo_url, payload.branch
47+
payload.repo.url, payload.branch
4548
);
4649
// FIXME 일단 interval_secs 는 유저가 수정할 수 없게 할 거긴 한데, 일단 테스트할 때 편하게 요청을 받아보자.
4750
let interval = payload.interval_secs.unwrap_or(120);
4851
let interval = Duration::from_secs(interval);
49-
if let Err(e) = spawn_repository_checker(&payload.repo_url, payload.branch, interval).await {
52+
if let Err(e) = spawn_repository_checker(&payload.repo.url, payload.branch, interval).await {
5053
error!("Failed to spawn repository checker: {}", e);
5154
return Err(StatusCode::BAD_REQUEST);
5255
}
@@ -55,12 +58,56 @@ async fn check_handler(Json(payload): Json<CheckRequest>) -> Result<&'static str
5558

5659
#[derive(Deserialize)]
5760
struct CancelRequest {
58-
repo_url: String,
61+
repo: RepositoryURL,
5962
branch: Option<String>,
6063
}
6164

65+
/// Represents a GitHub repository URL.
66+
///
67+
/// This struct ensures that the URL is valid and follows the format
68+
/// `https://github.com/{owner}/{repo_name}`. It includes validation logic
69+
/// to enforce this format.
70+
#[derive(Debug, Clone)]
71+
struct RepositoryURL {
72+
/// The URL of the repository.
73+
url: String,
74+
}
75+
76+
impl<'de> Deserialize<'de> for RepositoryURL {
77+
/// Custom deserialization logic for `RepositoryURL`.
78+
///
79+
/// This implementation ensures that the URL is validated during
80+
/// deserialization. If the URL is invalid, an error is returned.
81+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82+
where
83+
D: serde::Deserializer<'de>,
84+
{
85+
let url = String::deserialize(deserializer)?;
86+
let repo = RepositoryURL { url };
87+
repo.validate().map_err(serde::de::Error::custom)?;
88+
Ok(repo)
89+
}
90+
}
91+
92+
impl RepositoryURL {
93+
fn validate(&self) -> Result<(), String> {
94+
if !self.url.starts_with(GITHUB_BASE_URL) {
95+
return Err(format!("URL must start with {}", GITHUB_BASE_URL));
96+
}
97+
let parts: Vec<&str> = self
98+
.url
99+
.trim_start_matches(GITHUB_BASE_URL)
100+
.split('/')
101+
.collect();
102+
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
103+
return Err(format!("URL must be in format {}", GITHUB_URL_FORMAT));
104+
}
105+
Ok(())
106+
}
107+
}
108+
62109
async fn cancel_handler(Json(payload): Json<CancelRequest>) -> Result<&'static str, StatusCode> {
63-
if let Err(e) = cancel_repository_checker(&payload.repo_url, payload.branch).await {
110+
if let Err(e) = cancel_repository_checker(&payload.repo.url, payload.branch).await {
64111
error!("Repository checker failed: {}", e);
65112
return Err(StatusCode::BAD_REQUEST);
66113
}

rook/src/main_test.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use super::*;
4+
use serde_json::json;
5+
6+
#[test]
7+
fn test_repository_url_deserialization() {
8+
// Valid URLs
9+
let valid_url = json!({ "url": "https://github.com/owner/repo" });
10+
assert!(serde_json::from_value::<RepositoryURL>(valid_url).is_ok());
11+
12+
let valid_url = json!({ "url": "https://github.com/rust-lang/rust" });
13+
assert!(serde_json::from_value::<RepositoryURL>(valid_url).is_ok());
14+
15+
// Invalid URLs
16+
let invalid_url = json!({ "url": "https://gitlab.com/owner/repo" });
17+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
18+
19+
let invalid_url = json!({ "url": "https://github.com/" });
20+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
21+
22+
let invalid_url = json!({ "url": "https://github.com/owner" });
23+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
24+
25+
let invalid_url = json!({ "url": "https://github.com/owner/" });
26+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
27+
28+
let invalid_url = json!({ "url": "http://github.com/owner/repo" });
29+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
30+
31+
let invalid_url = json!({ "url": "https://github.com//repo" });
32+
assert!(serde_json::from_value::<RepositoryURL>(invalid_url).is_err());
33+
}
34+
}

0 commit comments

Comments
 (0)