use std::fs;
use std::path::Path;
use tempfile::TempDir;
use tokio;
use syncable_cli::analyzer::{
dependency_parser::{DependencyParser, Language},
runtime::{DetectionConfidence, JavaScriptRuntime, PackageManager, RuntimeDetector},
tool_management::ToolDetector,
vulnerability::VulnerabilityChecker,
};
#[tokio::test]
async fn test_bun_project_detection_and_audit_workflow() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_bun_project(project_path);
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Bun);
assert_eq!(detection_result.runtime, JavaScriptRuntime::Bun);
let mut tool_detector = ToolDetector::new();
let js_managers = tool_detector.detect_js_package_managers();
assert!(js_managers.contains_key("bun"));
assert!(js_managers.contains_key("npm"));
assert!(js_managers.contains_key("yarn"));
assert!(js_managers.contains_key("pnpm"));
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
assert!(dependencies.contains_key(&Language::JavaScript));
let js_deps = &dependencies[&Language::JavaScript];
assert!(!js_deps.is_empty());
assert!(js_deps.iter().any(|d| d.name == "express"));
assert!(js_deps.iter().any(|d| d.name == "lodash"));
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
let vulnerability_report = report.unwrap();
}
#[tokio::test]
async fn test_npm_project_detection_and_audit_workflow() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_npm_project(project_path);
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Npm);
assert_eq!(detection_result.runtime, JavaScriptRuntime::Node);
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
assert!(dependencies.contains_key(&Language::JavaScript));
let js_deps = &dependencies[&Language::JavaScript];
assert!(!js_deps.is_empty());
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
}
#[tokio::test]
async fn test_yarn_project_detection_and_audit_workflow() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_yarn_project(project_path);
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Yarn);
assert_eq!(detection_result.runtime, JavaScriptRuntime::Node);
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
}
#[tokio::test]
async fn test_pnpm_project_detection_and_audit_workflow() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_pnpm_project(project_path);
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Pnpm);
assert_eq!(detection_result.runtime, JavaScriptRuntime::Node);
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
}
#[tokio::test]
async fn test_multi_runtime_project_priority() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_multi_runtime_project(project_path);
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Bun);
assert_eq!(detection_result.runtime, JavaScriptRuntime::Bun);
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
}
#[tokio::test]
#[ignore] async fn test_vulnerability_checking_with_mixed_languages() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_polyglot_project(project_path);
let parser = DependencyParser::new();
let dependencies = parser.parse_all_dependencies(project_path).unwrap();
assert!(dependencies.contains_key(&Language::JavaScript));
assert!(dependencies.contains_key(&Language::Python));
assert!(dependencies.contains_key(&Language::Rust));
let checker = VulnerabilityChecker::new();
let report = checker
.check_all_dependencies(&dependencies, project_path)
.await;
assert!(report.is_ok());
let vulnerability_report = report.unwrap();
}
#[test]
fn test_tool_detection_comprehensive() {
let mut tool_detector = ToolDetector::new();
let js_tools = tool_detector.detect_js_package_managers();
assert_eq!(js_tools.len(), 4);
assert!(js_tools.contains_key("bun"));
assert!(js_tools.contains_key("npm"));
assert!(js_tools.contains_key("yarn"));
assert!(js_tools.contains_key("pnpm"));
let bun_status = tool_detector.detect_bun();
assert!(bun_status.last_checked.elapsed().unwrap().as_secs() < 5);
let bun_status_cached = tool_detector.detect_bun();
assert_eq!(bun_status.last_checked, bun_status_cached.last_checked);
tool_detector.clear_cache();
let bun_status_fresh = tool_detector.detect_bun();
assert!(bun_status_fresh.last_checked >= bun_status.last_checked);
}
#[test]
fn test_runtime_detection_edge_cases() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
let runtime_detector = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Unknown);
fs::write(
project_path.join("package.json"),
r#"{"name": "test", "version": "1.0.0"}"#,
)
.unwrap();
let runtime_detector_with_pkg = RuntimeDetector::new(project_path.to_path_buf());
let detection_result = runtime_detector_with_pkg.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Npm); assert_eq!(detection_result.runtime, JavaScriptRuntime::Node);
assert_eq!(detection_result.confidence, DetectionConfidence::Low);
fs::write(
project_path.join("package.json"),
r#"{"name": "test", "version": "1.0.0", "packageManager": "bun@1.0.0"}"#,
)
.unwrap();
let detection_result = runtime_detector_with_pkg.detect_js_runtime_and_package_manager();
assert_eq!(detection_result.package_manager, PackageManager::Bun);
}
fn create_bun_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "test-bun-project",
"version": "1.0.0",
"packageManager": "bun@1.0.0",
"engines": {
"bun": ">=1.0.0"
},
"scripts": {
"start": "bun run index.js",
"dev": "bun --watch index.js"
},
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/node": "^18.0.0",
"bun-types": "^1.0.0"
}
}"#,
)
.unwrap();
fs::write(
project_path.join("bun.lockb"),
"Binary lockfile content (simulated)",
)
.unwrap();
fs::write(
project_path.join("bunfig.toml"),
r#"[install]
cache = true
[install.scopes]
"@myorg" = { token = "$NPM_TOKEN", url = "https://registry.npmjs.org/" }
"#,
)
.unwrap();
}
fn create_npm_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "test-npm-project",
"version": "1.0.0",
"dependencies": {
"react": "^18.0.0",
"axios": "^1.0.0"
},
"devDependencies": {
"jest": "^29.0.0"
}
}"#,
)
.unwrap();
fs::write(
project_path.join("package-lock.json"),
r#"{
"name": "test-npm-project",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}"#,
)
.unwrap();
}
fn create_yarn_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "test-yarn-project",
"version": "1.0.0",
"packageManager": "yarn@3.6.0",
"dependencies": {
"vue": "^3.0.0",
"vuex": "^4.0.0"
}
}"#,
)
.unwrap();
fs::write(
project_path.join("yarn.lock"),
r#"# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
vue@^3.0.0:
version "3.3.4"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.3.4.tgz"
"#,
)
.unwrap();
}
fn create_pnpm_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "test-pnpm-project",
"version": "1.0.0",
"packageManager": "pnpm@8.0.0",
"dependencies": {
"svelte": "^4.0.0"
}
}"#,
)
.unwrap();
fs::write(
project_path.join("pnpm-lock.yaml"),
r#"lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
svelte:
specifier: ^4.0.0
version: 4.2.0
"#,
)
.unwrap();
}
fn create_multi_runtime_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "test-multi-runtime",
"version": "1.0.0",
"packageManager": "bun@1.0.0",
"engines": {
"bun": ">=1.0.0",
"node": ">=18.0.0"
},
"dependencies": {
"fastify": "^4.0.0"
}
}"#,
)
.unwrap();
fs::write(project_path.join("bun.lockb"), "bun lockfile").unwrap();
fs::write(project_path.join("yarn.lock"), "yarn lockfile").unwrap();
fs::write(project_path.join("pnpm-lock.yaml"), "pnpm lockfile").unwrap();
fs::write(project_path.join("package-lock.json"), "{}").unwrap();
}
fn create_polyglot_project(project_path: &Path) {
fs::write(
project_path.join("package.json"),
r#"{
"name": "polyglot-project",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.0"
}
}"#,
)
.unwrap();
fs::write(
project_path.join("requirements.txt"),
"flask==2.3.0\nrequests==2.31.0\n",
)
.unwrap();
fs::write(
project_path.join("Cargo.toml"),
r#"[package]
name = "polyglot-project"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1.0"
tokio = "1.0"
"#,
)
.unwrap();
fs::write(
project_path.join("go.mod"),
r#"module polyglot-project
go 1.19
require (
github.com/gin-gonic/gin v1.9.0
github.com/gorilla/mux v1.8.0
)
"#,
)
.unwrap();
}