#[cfg(test)]
mod tests {
use crate::encryption::{decrypt, encrypt};
use crate::lock::LockFile;
use crate::state::execution_state::{load_state, save_state};
use crate::types::*;
use std::collections::BTreeMap;
use std::path::PathBuf;
use tempfile::TempDir;
fn make_pkg(name: &str, version: &str) -> PlannedPackage {
PlannedPackage {
name: name.to_string(),
version: version.to_string(),
manifest_path: PathBuf::from(format!("crates/{}/Cargo.toml", name)),
}
}
fn make_state(plan_id: &str, packages: Vec<(String, String, PackageState)>) -> ExecutionState {
let mut pkg_map = BTreeMap::new();
for (name, version, state) in packages {
pkg_map.insert(
format!("{}@{}", name, version),
PackageProgress {
name,
version,
attempts: 0,
state,
last_updated_at: chrono::Utc::now(),
},
);
}
ExecutionState {
state_version: "shipper.state.v2".to_string(),
plan_id: plan_id.to_string(),
registry: Registry::crates_io(),
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
packages: pkg_map,
}
}
#[test]
fn stress_lock_acquire_release_cycle() {
let temp_dir = TempDir::new().unwrap();
let state_dir = temp_dir.path().join(".shipper");
for i in 0..100 {
let lock = LockFile::acquire(&state_dir, None)
.unwrap_or_else(|_| panic!("Failed to acquire lock on iteration {}", i));
drop(lock);
}
}
#[test]
fn stress_state_save_load_cycle() {
let temp_dir = TempDir::new().unwrap();
let state_dir = temp_dir.path().join(".shipper");
std::fs::create_dir_all(&state_dir).unwrap();
let packages: Vec<(String, String, PackageState)> = (0..50)
.map(|i| {
let state = if i % 3 == 0 {
PackageState::Published
} else if i % 3 == 1 {
PackageState::Pending
} else {
PackageState::Failed {
class: ErrorClass::Retryable,
message: "test".into(),
}
};
(format!("crate-{}", i), "1.0.0".to_string(), state)
})
.collect();
let state = make_state("stress-test", packages);
for i in 0..50 {
save_state(&state_dir, &state)
.unwrap_or_else(|_| panic!("Failed to save state on iteration {}", i));
let loaded = load_state(&state_dir)
.unwrap_or_else(|_| panic!("Failed to load state on iteration {}", i))
.expect("state should exist");
assert_eq!(loaded.plan_id, "stress-test");
assert_eq!(loaded.packages.len(), 50);
}
}
#[test]
fn stress_large_publish_level() {
let packages: Vec<PlannedPackage> = (0..100)
.map(|i| make_pkg(&format!("crate-{}", i), "1.0.0"))
.collect();
let level = PublishLevel { level: 0, packages };
let json = serde_json::to_string(&level).unwrap();
let parsed: PublishLevel = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.packages.len(), 100);
}
#[test]
fn stress_deep_dependency_chain() {
let mut deps = BTreeMap::new();
for i in 1..50 {
deps.insert(format!("crate-{}", i), vec![format!("crate-{}", i - 1)]);
}
let mut visited = std::collections::HashSet::new();
for name in deps.keys() {
assert!(!visited.contains(name), "Cycle detected at {}", name);
visited.insert(name.clone());
}
assert_eq!(visited.len(), 49); }
#[test]
fn stress_event_log_append() {
use crate::state::events::EventLog;
let temp_dir = TempDir::new().unwrap();
let log_path = temp_dir.path().join("events.jsonl");
let mut log = EventLog::new();
for i in 0..1000 {
let event = PublishEvent {
timestamp: chrono::Utc::now(),
event_type: EventType::PackageStarted {
name: format!("crate-{}", i % 100),
version: "1.0.0".to_string(),
},
package: format!("crate-{}@1.0.0", i % 100),
};
log.record(event);
}
log.write_to_file(&log_path).unwrap();
let loaded = EventLog::read_from_file(&log_path).unwrap();
assert_eq!(loaded.all_events().len(), 1000);
}
#[test]
fn stress_sequential_state_updates() {
let temp_dir = TempDir::new().unwrap();
let state_dir = temp_dir.path().join(".shipper");
std::fs::create_dir_all(&state_dir).unwrap();
let state = make_state(
"sequential-test",
vec![(
"core".to_string(),
"1.0.0".to_string(),
PackageState::Pending,
)],
);
save_state(&state_dir, &state).unwrap();
for i in 0..10 {
let mut local_state = load_state(&state_dir).unwrap().expect("state should exist");
local_state.plan_id = format!("sequential-test-{}", i);
save_state(&state_dir, &local_state).unwrap();
}
let final_state = load_state(&state_dir).unwrap().expect("state should exist");
assert!(final_state.plan_id.starts_with("sequential-test"));
}
#[test]
fn stress_receipt_with_many_packages() {
let temp_dir = TempDir::new().unwrap();
let receipt_path = temp_dir.path().join("receipt.json");
let packages: Vec<PackageReceipt> = (0..100)
.map(|i| PackageReceipt {
name: format!("crate-{}", i),
version: "1.0.0".to_string(),
attempts: 1,
state: PackageState::Published,
started_at: chrono::Utc::now(),
finished_at: chrono::Utc::now(),
duration_ms: 1000,
evidence: PackageEvidence {
attempts: vec![],
readiness_checks: vec![],
},
compromised_at: None,
compromised_by: None,
superseded_by: None,
})
.collect();
let receipt = Receipt {
receipt_version: "shipper.receipt.v2".to_string(),
plan_id: "stress-test".to_string(),
registry: Registry::crates_io(),
started_at: chrono::Utc::now(),
finished_at: chrono::Utc::now(),
packages,
event_log_path: receipt_path.clone(),
git_context: None,
environment: EnvironmentFingerprint {
shipper_version: env!("CARGO_PKG_VERSION").to_string(),
cargo_version: None,
rust_version: None,
os: std::env::consts::OS.to_string(),
arch: std::env::consts::ARCH.to_string(),
},
};
let json = serde_json::to_string(&receipt).unwrap();
assert!(json.len() > 10_000);
let parsed: Receipt = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.packages.len(), 100);
}
#[test]
fn stress_encryption_roundtrip_large_data() {
let large_state = make_state(
"large-test",
(0..50)
.map(|i| {
(
format!("crate-{}", i),
"1.0.0".to_string(),
PackageState::Pending,
)
})
.collect(),
);
let json = serde_json::to_string(&large_state).unwrap();
let passphrase = "stress-test-passphrase";
for i in 0..10 {
let encrypted = encrypt(json.as_bytes(), passphrase)
.unwrap_or_else(|_| panic!("Encryption failed on iteration {}", i));
assert!(encrypted.len() > json.len());
let encrypted_str = String::from_utf8(encrypted).unwrap();
let decrypted = decrypt(&encrypted_str, passphrase)
.unwrap_or_else(|_| panic!("Decryption failed on iteration {}", i));
assert_eq!(decrypted, json.as_bytes());
}
}
}