Skip to content

Commit ba52656

Browse files
Improve test architecture and add environment configuration (#48)
* refactor: Translate Korean comments to English in git.rs * test: Use tempfile for automatic test cleanup * refactor: Improve test isolation and dependency injection - Replace per-request service creation with dependency injection - Add .env support for UPLOAD_DIR configuration - Implement sequential test execution to prevent race conditions - Auto-generate test problem IDs using rand * docs: Translate Korean comments to English in test files * test: Change content-type to text/plain * refactor: Remove rand crate
1 parent 98d90b2 commit ba52656

File tree

9 files changed

+658
-371
lines changed

9 files changed

+658
-371
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
DATABASE_URL="postgres://postgres:password@localhost:5432/coduck"
2+
UPLOAD_DIR="uploads"

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ name = "coduck-backend"
1414
anyhow = "1.0"
1515
axum = { version = "0.8.4", features = ["json", "multipart"] }
1616
chrono = { version = "0.4.38", features = ["serde"] }
17+
dotenv = "0.15.0"
1718
git2 = "0.20.2"
1819
serde = { version = "1.0.219", features = ["derive"] }
1920
serde_json = "1.0.133"
@@ -23,3 +24,5 @@ uuid = { version = "1.17.0", features = ["v4"] }
2324
[dev-dependencies]
2425
reqwest = { version = "0.12.19", features = ["json", "rustls-tls"] }
2526
rstest = "0.25.0"
27+
serial_test = "3.2"
28+
tempfile = "3.20"

src/file_manager/git.rs

Lines changed: 172 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@ use git2::{DiffOptions, IndexAddOption, Repository, StatusOptions, Time};
55
use std::path::PathBuf;
66
use tokio::fs;
77

8-
const UPLOAD_DIR: &str = "uploads";
98
const DEFAULT_DIRECTORIES: [&str; 3] = ["solutions", "tests", "statements"];
109

1110
#[derive(Debug)]
1211
struct GitManager {
1312
problem_id: u32,
13+
base_path: PathBuf,
1414
}
1515

1616
impl GitManager {
17-
fn new(problem_id: u32) -> Self {
18-
Self { problem_id }
17+
fn new(problem_id: u32, base_path: PathBuf) -> Self {
18+
Self {
19+
problem_id,
20+
base_path,
21+
}
22+
}
23+
24+
fn get_upload_path(&self) -> PathBuf {
25+
self.base_path.join(self.problem_id.to_string())
1926
}
2027

2128
fn git_init(&self) -> Result<()> {
22-
let path = PathBuf::from(UPLOAD_DIR).join(self.problem_id.to_string());
29+
let path = self.get_upload_path();
2330
Repository::init(&path)
2431
.and_then(|repo| {
2532
let mut config = repo.config()?;
@@ -28,7 +35,7 @@ impl GitManager {
2835
Ok(repo)
2936
})
3037
.map(|_| ())
31-
.with_context(|| format!("Failed to init git repo at {:?}", path))
38+
.with_context(|| format!("Failed to init git repo at {path:?}"))
3239
}
3340

3441
async fn create_problem(&self) -> Result<()> {
@@ -127,12 +134,12 @@ impl GitManager {
127134
}
128135

129136
async fn create_default_directories(&self) -> Result<()> {
130-
let base_path = PathBuf::from(UPLOAD_DIR).join(self.problem_id.to_string());
137+
let base_path = self.get_upload_path();
131138
for dir in DEFAULT_DIRECTORIES {
132139
let path = base_path.join(dir);
133140
fs::create_dir_all(path)
134141
.await
135-
.with_context(|| format!("Failed to create directory: {}", dir))?;
142+
.with_context(|| format!("Failed to create directory: {dir}"))?;
136143
}
137144
Ok(())
138145
}
@@ -169,9 +176,9 @@ impl GitManager {
169176
}
170177

171178
fn get_repository(&self) -> Result<Repository> {
172-
let path = PathBuf::from(UPLOAD_DIR).join(self.problem_id.to_string());
179+
let path = self.get_upload_path();
173180
Repository::open(&path)
174-
.with_context(|| format!("Failed to open git repository at {:?}", path))
181+
.with_context(|| format!("Failed to open git repository at {path:?}"))
175182
}
176183
}
177184

@@ -194,6 +201,7 @@ mod tests {
194201
use super::*;
195202
use rstest::rstest;
196203
use std::path::Path;
204+
use tempfile::TempDir;
197205
use tokio::fs;
198206

199207
#[rstest]
@@ -223,20 +231,24 @@ mod tests {
223231

224232
#[tokio::test]
225233
async fn can_init_git_repository() -> Result<(), std::io::Error> {
226-
let problem_id = 10;
227-
let git_manager = GitManager::new(problem_id);
234+
let temp_dir = TempDir::new()?;
235+
let problem_id = 0;
236+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
228237
assert!(git_manager.git_init().is_ok());
229-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}").as_str()).exists());
230-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/.git").as_str()).exists());
238+
assert!(Path::new(git_manager.get_upload_path().to_str().unwrap()).exists());
239+
assert!(Path::new(
240+
format!("{}/.git", git_manager.get_upload_path().to_str().unwrap()).as_str()
241+
)
242+
.exists());
231243

232-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
233244
Ok(())
234245
}
235246

236247
#[tokio::test]
237248
async fn can_set_config() -> Result<(), std::io::Error> {
238-
let problem_id = 11;
239-
let git_manager = GitManager::new(problem_id);
249+
let temp_dir = TempDir::new()?;
250+
let problem_id = 0;
251+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
240252
assert!(git_manager.git_init().is_ok());
241253
let repo = git_manager.get_repository().unwrap();
242254
let config = repo.config().unwrap();
@@ -246,48 +258,103 @@ mod tests {
246258
Ok("[email protected]".to_string())
247259
);
248260

249-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
250261
Ok(())
251262
}
252263

253264
#[tokio::test]
254265
async fn can_create_default_file() -> Result<(), std::io::Error> {
255-
let problem_id = 12;
256-
let git_manager = GitManager::new(problem_id);
266+
let temp_dir = TempDir::new()?;
267+
let problem_id = 0;
268+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
257269
assert!(git_manager.create_default_directories().await.is_ok());
258-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/solutions").as_str()).exists());
259-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/tests").as_str()).exists());
260-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/statements").as_str()).exists());
270+
assert!(Path::new(
271+
format!(
272+
"{}/solutions",
273+
git_manager.get_upload_path().to_str().unwrap()
274+
)
275+
.as_str()
276+
)
277+
.exists());
278+
assert!(Path::new(
279+
format!("{}/tests", git_manager.get_upload_path().to_str().unwrap()).as_str()
280+
)
281+
.exists());
282+
assert!(Path::new(
283+
format!(
284+
"{}/statements",
285+
git_manager.get_upload_path().to_str().unwrap()
286+
)
287+
.as_str()
288+
)
289+
.exists());
261290

262-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
263291
Ok(())
264292
}
265293

266294
#[tokio::test]
267295
async fn can_create_problem() -> Result<(), std::io::Error> {
268-
let problem_id = 13;
269-
let git_manager = GitManager::new(problem_id);
296+
let temp_dir = TempDir::new()?;
297+
let problem_id = 0;
298+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
270299
assert!(git_manager.create_problem().await.is_ok());
271-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}").as_str()).exists());
272-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/.git").as_str()).exists());
273-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/solutions").as_str()).exists());
274-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/tests").as_str()).exists());
275-
assert!(Path::new(format!("{UPLOAD_DIR}/{problem_id}/statements").as_str()).exists());
300+
assert!(Path::new(git_manager.get_upload_path().to_str().unwrap()).exists());
301+
assert!(Path::new(
302+
format!("{}/.git", git_manager.get_upload_path().to_str().unwrap()).as_str()
303+
)
304+
.exists());
305+
assert!(Path::new(
306+
format!(
307+
"{}/solutions",
308+
git_manager.get_upload_path().to_str().unwrap()
309+
)
310+
.as_str()
311+
)
312+
.exists());
313+
assert!(Path::new(
314+
format!("{}/tests", git_manager.get_upload_path().to_str().unwrap()).as_str()
315+
)
316+
.exists());
317+
assert!(Path::new(
318+
format!(
319+
"{}/statements",
320+
git_manager.get_upload_path().to_str().unwrap()
321+
)
322+
.as_str()
323+
)
324+
.exists());
276325

277-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
278326
Ok(())
279327
}
280328

281329
#[tokio::test]
282330
async fn can_get_git_status() -> Result<(), tokio::io::Error> {
283-
let problem_id = 14;
284-
let git_manager = GitManager::new(problem_id);
331+
let temp_dir = TempDir::new()?;
332+
let problem_id = 0;
333+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
285334

286335
git_manager.git_init().unwrap();
287336

288-
fs::create_dir_all(format!("{UPLOAD_DIR}/{problem_id}/tests")).await?;
289-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.in"), "1 2").await?;
290-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.out"), "3").await?;
337+
fs::create_dir_all(format!(
338+
"{}/tests",
339+
git_manager.get_upload_path().to_str().unwrap()
340+
))
341+
.await?;
342+
fs::write(
343+
format!(
344+
"{}/tests/1.in",
345+
git_manager.get_upload_path().to_str().unwrap()
346+
),
347+
"1 2",
348+
)
349+
.await?;
350+
fs::write(
351+
format!(
352+
"{}/tests/1.out",
353+
git_manager.get_upload_path().to_str().unwrap()
354+
),
355+
"3",
356+
)
357+
.await?;
291358

292359
let file_infos = git_manager.git_status().unwrap();
293360
let expected = vec![
@@ -302,47 +369,83 @@ mod tests {
302369
];
303370
assert_eq!(file_infos, expected);
304371

305-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
306372
Ok(())
307373
}
308374

309375
#[tokio::test]
310376
async fn can_git_add() -> Result<(), tokio::io::Error> {
311-
let problem_id = 15;
312-
let git_manager = GitManager::new(problem_id);
377+
let temp_dir = TempDir::new()?;
378+
let problem_id = 0;
379+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
313380

314381
git_manager.git_init().unwrap();
315382

316383
let repo = git_manager.get_repository().unwrap();
317-
fs::create_dir_all(format!("{UPLOAD_DIR}/{problem_id}/tests")).await?;
318-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.in"), "1 2").await?;
319-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.out"), "3").await?;
384+
fs::create_dir_all(format!(
385+
"{}/tests",
386+
git_manager.get_upload_path().to_str().unwrap()
387+
))
388+
.await?;
389+
fs::write(
390+
format!(
391+
"{}/tests/1.in",
392+
git_manager.get_upload_path().to_str().unwrap()
393+
),
394+
"1 2",
395+
)
396+
.await?;
397+
fs::write(
398+
format!(
399+
"{}/tests/1.out",
400+
git_manager.get_upload_path().to_str().unwrap()
401+
),
402+
"3",
403+
)
404+
.await?;
320405

321406
assert!(git_manager.git_add_all().is_ok());
322407

323408
let statuses = repo.statuses(None).unwrap();
324409

325-
// 워킹 디렉토리에 존재하지 않아야 한다.
410+
// Should not exist in working directory
326411
assert!(!statuses.iter().any(|e| e.status().is_wt_new()));
327412
assert!(!statuses.iter().any(|e| e.status().is_wt_modified()));
328413
assert!(!statuses.iter().any(|e| e.status().is_wt_deleted()));
329414

330-
// 스테이징 영역에 올라와야 한다.
415+
// Should be in staging area
331416
assert!(statuses.iter().all(|e| e.status().is_index_new()));
332417

333-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
334418
Ok(())
335419
}
336420

337421
#[tokio::test]
338422
async fn can_commit() -> Result<(), tokio::io::Error> {
339-
let problem_id = 16;
340-
let git_manager = GitManager::new(problem_id);
423+
let temp_dir = TempDir::new()?;
424+
let problem_id = 0;
425+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
341426
git_manager.git_init().unwrap();
342427

343-
fs::create_dir_all(format!("{UPLOAD_DIR}/{problem_id}/tests")).await?;
344-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.in"), "1 2").await?;
345-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.out"), "3").await?;
428+
fs::create_dir_all(format!(
429+
"{}/tests",
430+
git_manager.get_upload_path().to_str().unwrap()
431+
))
432+
.await?;
433+
fs::write(
434+
format!(
435+
"{}/tests/1.in",
436+
git_manager.get_upload_path().to_str().unwrap()
437+
),
438+
"1 2",
439+
)
440+
.await?;
441+
fs::write(
442+
format!(
443+
"{}/tests/1.out",
444+
git_manager.get_upload_path().to_str().unwrap()
445+
),
446+
"3",
447+
)
448+
.await?;
346449

347450
let commit_message = "add test 1";
348451

@@ -354,19 +457,33 @@ mod tests {
354457

355458
assert_eq!(commit.message(), Some(commit_message));
356459

357-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
358460
Ok(())
359461
}
360462

361463
#[tokio::test]
362464
async fn can_get_log() -> Result<(), tokio::io::Error> {
363-
let problem_id = 17;
364-
let git_manager = GitManager::new(problem_id);
465+
let temp_dir = TempDir::new()?;
466+
let problem_id = 0;
467+
let git_manager = GitManager::new(problem_id, temp_dir.path().to_path_buf());
365468
git_manager.git_init().unwrap();
366469
git_manager.create_default_directories().await.unwrap();
367470

368-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.in"), "1 2").await?;
369-
fs::write(format!("{UPLOAD_DIR}/{problem_id}/tests/1.out"), "3").await?;
471+
fs::write(
472+
format!(
473+
"{}/tests/1.in",
474+
git_manager.get_upload_path().to_str().unwrap()
475+
),
476+
"1 2",
477+
)
478+
.await?;
479+
fs::write(
480+
format!(
481+
"{}/tests/1.out",
482+
git_manager.get_upload_path().to_str().unwrap()
483+
),
484+
"3",
485+
)
486+
.await?;
370487

371488
git_manager
372489
.git_commit("create default file".to_string())
@@ -386,7 +503,6 @@ mod tests {
386503
];
387504
assert_eq!(log[0].paths, expected_path);
388505

389-
fs::remove_dir_all(format!("{UPLOAD_DIR}/{problem_id}")).await?;
390506
Ok(())
391507
}
392508
}

0 commit comments

Comments
 (0)