use std::fs;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
pub struct TempProject {
pub dir: TempDir,
}
impl TempProject {
pub fn new() -> Self {
let dir = TempDir::new().expect("Failed to create temp directory");
Self { dir }
}
pub fn path(&self) -> &Path {
self.dir.path()
}
pub fn create_file(&self, relative_path: &str, content: &str) {
let file_path = self.dir.path().join(relative_path);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent).expect("Failed to create parent directories");
}
fs::write(&file_path, content).expect("Failed to write file");
}
pub fn file_path(&self, relative_path: &str) -> PathBuf {
self.dir.path().join(relative_path)
}
}
impl Default for TempProject {
fn default() -> Self {
Self::new()
}
}
pub fn sample_requirements_txt() -> &'static str {
r#"# Sample requirements.txt
requests>=2.28.0,<3.0.0
numpy==1.24.0
flask^2.0.0
Django~=4.0.0
pandas>=1.5.0
pytest==7.4.0
black>=23.0.0
mypy~=1.4.0
click>=8.0.0,<9.0.0
pydantic==2.0.0
"#
}
pub fn sample_requirements_dev_txt() -> &'static str {
r#"# Development dependencies
pytest>=7.0.0
pytest-cov==4.1.0
black==23.7.0
mypy>=1.0.0
ruff>=0.0.280
"#
}
pub fn sample_pyproject_pep621() -> &'static str {
r#"[project]
name = "test-project"
version = "0.1.0"
description = "A test project"
dependencies = [
"requests>=2.28.0,<3.0.0",
"numpy==1.24.0",
"pandas>=1.5.0",
"click>=8.0.0,<9.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"black==23.7.0",
"mypy>=1.0.0",
]
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
"#
}
pub fn sample_pyproject_poetry() -> &'static str {
r#"[tool.poetry]
name = "test-project"
version = "0.1.0"
description = "A test project"
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.28.0"
numpy = "1.24.0"
pandas = ">=1.5.0"
django = "~4.0.0"
flask = "^2.0.0"
[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
black = "23.7.0"
mypy = "~1.4.0"
ruff = ">=0.0.280"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
"#
}
pub fn sample_pyproject_pdm() -> &'static str {
r#"[project]
name = "test-project"
version = "0.1.0"
description = "A test project"
dependencies = [
"requests>=2.28.0,<3.0.0",
"numpy==1.24.0",
"pandas>=1.5.0",
]
[tool.pdm.dev-dependencies]
dev = [
"pytest>=7.0.0",
"black==23.7.0",
]
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
"#
}
pub fn sample_environment_yml() -> &'static str {
r#"name: test-env
channels:
- conda-forge
- defaults
dependencies:
- python=3.9
- numpy=1.24.0
- pandas>=1.5.0
- requests>=2.28.0
- pytest>=7.0.0
- pip:
- flask>=2.0.0
- black==23.7.0
"#
}
pub fn sample_uv_lock() -> &'static str {
r#"version = 1
[[package]]
name = "requests"
version = "2.28.0"
source = { registry = "https://pypi.org/simple" }
[[package]]
name = "numpy"
version = "1.24.0"
source = { registry = "https://pypi.org/simple" }
[[package]]
name = "pandas"
version = "1.5.3"
source = { registry = "https://pypi.org/simple" }
"#
}
pub fn sample_poetry_lock() -> &'static str {
r#"[[package]]
name = "requests"
version = "2.28.0"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7"
[[package]]
name = "numpy"
version = "1.24.0"
description = "Fundamental package for array computing in Python"
category = "main"
optional = false
python-versions = ">=3.8"
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "abc123"
"#
}
pub fn create_temp_project_with_requirements() -> TempProject {
let project = TempProject::new();
project.create_file("requirements.txt", sample_requirements_txt());
project
}
pub fn create_temp_project_with_pep621() -> TempProject {
let project = TempProject::new();
project.create_file("pyproject.toml", sample_pyproject_pep621());
project
}
pub fn create_temp_project_with_poetry() -> TempProject {
let project = TempProject::new();
project.create_file("pyproject.toml", sample_pyproject_poetry());
project.create_file("poetry.lock", sample_poetry_lock());
project
}
pub fn create_temp_project_with_pdm() -> TempProject {
let project = TempProject::new();
project.create_file("pyproject.toml", sample_pyproject_pdm());
project
}
pub fn create_temp_project_with_conda() -> TempProject {
let project = TempProject::new();
project.create_file("environment.yml", sample_environment_yml());
project
}
pub fn create_temp_project_with_multiple_files() -> TempProject {
let project = TempProject::new();
project.create_file("requirements.txt", sample_requirements_txt());
project.create_file("requirements-dev.txt", sample_requirements_dev_txt());
project.create_file("pyproject.toml", sample_pyproject_pep621());
project
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temp_project_creation() {
let project = TempProject::new();
assert!(project.path().exists());
assert!(project.path().is_dir());
}
#[test]
fn test_create_file() {
let project = TempProject::new();
project.create_file("test.txt", "hello world");
let file_path = project.file_path("test.txt");
assert!(file_path.exists());
let content = fs::read_to_string(file_path).unwrap();
assert_eq!(content, "hello world");
}
#[test]
fn test_create_file_with_subdirs() {
let project = TempProject::new();
project.create_file("subdir/test.txt", "hello");
let file_path = project.file_path("subdir/test.txt");
assert!(file_path.exists());
}
#[test]
fn test_sample_fixtures_are_valid() {
assert!(!sample_requirements_txt().is_empty());
assert!(!sample_pyproject_pep621().is_empty());
assert!(!sample_pyproject_poetry().is_empty());
assert!(!sample_environment_yml().is_empty());
}
}