#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use crate::marketplace::registry::Registry;
use crate::marketplace::types::{Checksum, NonEmptyQuery, SemanticVersion, ValidatedPackageName};
use std::sync::Arc;
use tempfile::TempDir;
use tokio::sync::Barrier;
#[tokio::test]
async fn test_lockfile_cleanup_on_error() {
let temp_dir = TempDir::new().unwrap();
let packages_dir = temp_dir.path().join("packages");
tokio::fs::create_dir_all(&packages_dir).await.unwrap();
let lockfile_path = packages_dir.join("ggen.lock");
let valid_lockfile = r#"{"version":"1.0","packages":{"test@1.0.0":{"name":"test","version":"1.0.0","resolved":"github:test","integrity":"abc123","dependencies":{}}}}"#;
tokio::fs::write(&lockfile_path, valid_lockfile)
.await
.unwrap();
assert!(lockfile_path.exists(), "Lockfile should exist");
let content = tokio::fs::read_to_string(&lockfile_path).await.unwrap();
assert!(
content.contains("test@1.0.0"),
"Lockfile should contain test package"
);
}
#[tokio::test]
async fn test_registry_save_cleanup_on_error() {
let temp_dir = TempDir::new().unwrap();
let index_path = temp_dir.path().join("index.json");
let empty_index = r#"{"version":"1.0.0","updated_at":"2024-01-01T00:00:00Z","packages":{}}"#;
tokio::fs::write(&index_path, empty_index).await.unwrap();
let registry = Registry::with_path(index_path.clone());
registry.load().await.unwrap();
let result = registry.save().await;
assert!(result.is_ok(), "Registry save should succeed");
let temp_path = index_path.with_extension("json.tmp");
assert!(
!temp_path.exists(),
"Temp file should be cleaned up after save"
);
}
#[tokio::test]
async fn test_concurrent_lockfile_writes() {
let temp_dir = TempDir::new().unwrap();
let packages_dir = temp_dir.path().join("packages");
tokio::fs::create_dir_all(&packages_dir).await.unwrap();
let lockfile_path = packages_dir.join("ggen.lock");
let initial_lockfile = r#"{"version":"1.0","packages":{}}"#;
tokio::fs::write(&lockfile_path, initial_lockfile)
.await
.unwrap();
let barrier = Arc::new(Barrier::new(5));
let mut handles = vec![];
for i in 0..5 {
let packages_dir = packages_dir.clone();
let barrier = Arc::clone(&barrier);
let handle = tokio::spawn(async move {
barrier.wait().await;
let lockfile_path = packages_dir.join("ggen.lock");
let temp_path = lockfile_path.with_extension("lock.tmp");
let content = format!(
r#"{{"version":"1.0","packages":{{"pkg{}@1.0.0":{{"name":"pkg{}","version":"1.0.0","resolved":"github:pkg{}","integrity":"abc123","dependencies":{{}}}}}}}}"#,
i, i, i
);
tokio::fs::write(&temp_path, content)
.await
.map_err(|e| ggen_utils::error::Error::new(&format!("Write error: {}", e)))?;
tokio::fs::rename(&temp_path, &lockfile_path)
.await
.map_err(|e| ggen_utils::error::Error::new(&format!("Rename error: {}", e)))?;
Ok::<(), ggen_utils::error::Error>(())
});
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
results.push(handle.await);
}
let success_count = results.iter().filter(|r| r.is_ok()).count();
assert!(
success_count > 0,
"At least one write should succeed (file locking prevents corruption)"
);
let content = tokio::fs::read_to_string(&lockfile_path).await.unwrap();
let _parsed: serde_json::Value = serde_json::from_str(&content)
.expect("Lockfile should be valid JSON after concurrent writes");
}
#[tokio::test]
async fn test_concurrent_registry_saves() {
let temp_dir = TempDir::new().unwrap();
let index_path = temp_dir.path().join("index.json");
let empty_index = r#"{"version":"1.0.0","updated_at":"2024-01-01T00:00:00Z","packages":{}}"#;
tokio::fs::write(&index_path, empty_index).await.unwrap();
let registry = Arc::new(Registry::with_path(index_path.clone()));
registry.load().await.unwrap();
let barrier = Arc::new(Barrier::new(3));
let mut handles = vec![];
for _i in 0..3 {
let registry = Arc::clone(®istry);
let barrier = Arc::clone(&barrier);
let handle = tokio::spawn(async move {
barrier.wait().await;
registry.save().await
});
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
results.push(handle.await);
}
let success_count = results.iter().filter(|r| r.is_ok()).count();
assert!(
success_count > 0,
"At least one save should succeed (file locking prevents corruption)"
);
let content = tokio::fs::read_to_string(&index_path).await.unwrap();
let _parsed: serde_json::Value = serde_json::from_str(&content)
.expect("Registry should be valid JSON after concurrent saves");
}
#[tokio::test]
async fn test_lockfile_corruption_recovery() {
let temp_dir = TempDir::new().unwrap();
let packages_dir = temp_dir.path().join("packages");
tokio::fs::create_dir_all(&packages_dir).await.unwrap();
let lockfile_path = packages_dir.join("ggen.lock");
let corrupted_content = "{ invalid json }";
tokio::fs::write(&lockfile_path, corrupted_content)
.await
.unwrap();
let backup_path = lockfile_path.with_extension("lock.backup");
let valid_backup = r#"{"version":"1.0","packages":{"test@1.0.0":{"name":"test","version":"1.0.0","resolved":"github:test","integrity":"abc123","dependencies":{}}}}"#;
tokio::fs::write(&backup_path, valid_backup).await.unwrap();
let backup_content = tokio::fs::read_to_string(&backup_path).await.unwrap();
let backup_valid: Result<serde_json::Value, _> = serde_json::from_str(&backup_content);
assert!(backup_valid.is_ok(), "Backup should be valid JSON");
let main_content = tokio::fs::read_to_string(&lockfile_path).await.unwrap();
let main_valid: Result<serde_json::Value, _> = serde_json::from_str(&main_content);
if main_valid.is_err() && backup_valid.is_ok() {
tokio::fs::write(&lockfile_path, &backup_content)
.await
.unwrap();
}
let restored_content = tokio::fs::read_to_string(&lockfile_path).await.unwrap();
assert!(
restored_content.contains("test@1.0.0"),
"Main lockfile should be restored from backup"
);
}
#[tokio::test]
async fn test_registry_corruption_recovery() {
let temp_dir = TempDir::new().unwrap();
let index_path = temp_dir.path().join("index.json");
let corrupted_content = "{ invalid json }";
tokio::fs::write(&index_path, corrupted_content)
.await
.unwrap();
let registry = Registry::with_path(index_path.clone());
let result = registry.load().await;
assert!(result.is_err(), "Should fail on corrupted registry");
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("corrupted") || error_msg.contains("invalid JSON"),
"Error message should indicate corruption: {}",
error_msg
);
}
}
#[tokio::test]
async fn test_real_file_io_lockfile_operations() {
let temp_dir = TempDir::new().unwrap();
let packages_dir = temp_dir.path().join("packages");
tokio::fs::create_dir_all(&packages_dir).await.unwrap();
let lockfile_json = r#"{"version":"1.0","packages":{"test@1.0.0":{"name":"test","version":"1.0.0","resolved":"github:test","integrity":"abc123","dependencies":{}}}}"#;
let lockfile_path = packages_dir.join("ggen.lock");
tokio::fs::write(&lockfile_path, lockfile_json)
.await
.unwrap();
let loaded_content = tokio::fs::read_to_string(&lockfile_path).await.unwrap();
let loaded: serde_json::Value = serde_json::from_str(&loaded_content).unwrap();
assert_eq!(loaded["version"], "1.0");
assert!(loaded["packages"]["test@1.0.0"].is_object());
assert_eq!(loaded["packages"]["test@1.0.0"]["name"], "test");
}
#[tokio::test]
async fn test_real_json_parsing_registry_operations() {
let temp_dir = TempDir::new().unwrap();
let index_path = temp_dir.path().join("index.json");
let registry = Registry::with_path(index_path.clone());
let checksum_64 = "a".repeat(64);
let valid_json = format!(
r#"{{
"version": "1.0.0",
"updated_at": "2024-01-01T00:00:00Z",
"packages": {{
"test-package": {{
"name": "test-package",
"description": "Test package description",
"versions": [
{{
"version": "1.0.0",
"download_url": "https://example.com/test.zip",
"checksum": "{}",
"dependencies": [],
"published_at": "2024-01-01T00:00:00Z",
"size_bytes": 1024
}}
],
"tags": []
}}
}}
}}"#,
checksum_64
);
tokio::fs::write(&index_path, valid_json).await.unwrap();
let result = registry.load().await;
if let Err(e) = &result {
eprintln!("Registry load error: {}", e);
}
assert!(result.is_ok(), "Should parse valid JSON registry");
}
#[tokio::test]
async fn test_validated_types_with_real_validation() {
let invalid_name = "package/../other";
assert!(
ValidatedPackageName::new(invalid_name).is_err(),
"Should reject path traversal in package name"
);
let invalid_version = "1.0";
assert!(
SemanticVersion::new(invalid_version).is_err(),
"Should reject invalid semver format"
);
let invalid_query = " ";
assert!(
NonEmptyQuery::new(invalid_query).is_err(),
"Should reject whitespace-only query"
);
let invalid_checksum = "abc";
assert!(
Checksum::new(invalid_checksum).is_err(),
"Should reject invalid checksum length"
);
}
#[tokio::test]
async fn test_end_to_end_error_scenarios() {
use crate::marketplace::search::search_packages;
use crate::marketplace::search::SearchFilters;
let result = search_packages("", &SearchFilters::new()).await;
assert!(result.is_err(), "Empty query should fail");
if let Err(e) = result {
assert!(
e.to_string().contains("empty"),
"Error should mention empty query"
);
}
let temp_dir = TempDir::new().unwrap();
let registry_path = temp_dir.path().join("registry");
tokio::fs::create_dir_all(®istry_path).await.unwrap();
let checksum_64 = "a".repeat(64);
let index_json = format!(
r#"{{"updated":"2024-01-01T00:00:00Z","packages":[{{"name":"test-package","version":"1.0.0","description":"Test","category":"test","author":"test","downloads":0,"stars":0,"path":"test","download_url":"https://example.com/test.zip","checksum":"{}"}}]}}"#,
checksum_64
);
let index_path = registry_path.join("index.json");
tokio::fs::write(&index_path, index_json).await.unwrap();
let index_content = tokio::fs::read_to_string(&index_path).await.unwrap();
let index: serde_json::Value = serde_json::from_str(&index_content).unwrap();
let packages = index["packages"].as_array().unwrap();
let exists = packages
.iter()
.any(|pkg| pkg["name"] == "test-package" && pkg["version"] == "1.0.0");
assert!(exists, "Should detect existing version");
}