//! SOLID Architecture Boundary Tests //! //! Scans the source tree to detect violations of the service separation //! constraints defined in the architecture plan. //! //! Run: `cargo test --test solid_boundaries` use std::path::{Path, PathBuf}; fn workspace_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) } fn collect_files(root: &Path, ext: &str) -> Vec { let mut files = Vec::new(); collect_recursive(root, ext, &mut files); files } fn collect_recursive(dir: &Path, ext: &str, out: &mut Vec) { let Ok(entries) = std::fs::read_dir(dir) else { return; }; for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { // Skip target, .git, test directories that contain expected violations for testing let name = path.file_name().unwrap_or_default().to_string_lossy(); if matches!(name.as_ref(), "target" | ".git" | "node_modules") { continue; } collect_recursive(&path, ext, out); } else if path.extension().and_then(|e| e.to_str()) == Some(ext) { out.push(path); } } } fn file_contains_any(path: &Path, patterns: &[&str]) -> bool { let Ok(content) = std::fs::read_to_string(path) else { return false; }; patterns.iter().any(|p| content.contains(p)) } fn is_within(path: &Path, segment: &str) -> bool { path.to_string_lossy().contains(segment) } #[test] fn no_provider_imports_outside_orchestrator() { let root = workspace_root(); let provider_patterns = ["hcloud_api", "hcloud::"]; let mut violations = Vec::new(); for rs in collect_files(&root, "rs") { if is_within(&rs, "orchestrator") || is_within(&rs, "tests/architecture") { continue; } if file_contains_any(&rs, &provider_patterns) { violations.push(rs.to_string_lossy().to_string()); } } assert!( violations.is_empty(), "SOLID violation — provider imports outside orchestrator:\n{}", violations.join("\n") ); } #[test] fn no_ssh_imports_outside_orchestrator() { let root = workspace_root(); let ssh_patterns = ["russh::", "ssh2::"]; let mut violations = Vec::new(); for rs in collect_files(&root, "rs") { if is_within(&rs, "orchestrator") || is_within(&rs, "tests/architecture") { continue; } if file_contains_any(&rs, &ssh_patterns) { violations.push(rs.to_string_lossy().to_string()); } } assert!( violations.is_empty(), "SOLID violation — SSH imports outside orchestrator:\n{}", violations.join("\n") ); } #[test] fn no_direct_surrealdb_in_nushell_cli() { let root = workspace_root(); let surreal_patterns = ["surrealdb", "ws://localhost:8000", "Surreal::new"]; let mut violations = Vec::new(); for nu in collect_files(&root, "nu") { // Allow platform service scripts to reference surrealdb if is_within(&nu, "platform/") || is_within(&nu, "tests/") { continue; } if file_contains_any(&nu, &surreal_patterns) { violations.push(nu.to_string_lossy().to_string()); } } assert!( violations.is_empty(), "SOLID violation — direct SurrealDB access in Nushell CLI:\n{}", violations.join("\n") ); }