Skip to main content
Glama
8b-is
by 8b-is
test_st_context_aware.rs•18.9 kB
// Test suite for ST Context-Aware System // "Context is everything - and everything needs testing!" - Testy McTesterson đź§Ş use anyhow::Result; use st::st_context_aware::{ ContextualOperation, ContextualStCommand, ProjectKnowledge, StContextTracker, WorkContext, }; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::SystemTime; use tempfile::TempDir; fn create_test_operation(op: &str, path: &str) -> ContextualOperation { ContextualOperation { timestamp: SystemTime::now(), operation: op.to_string(), path: PathBuf::from(path), result_summary: "Success".to_string(), context_hints: vec![], } } fn create_test_project() -> Result<TempDir> { let temp_dir = TempDir::new()?; // Create a typical project structure fs::create_dir_all(temp_dir.path().join("src/core"))?; fs::create_dir_all(temp_dir.path().join("src/utils"))?; fs::create_dir_all(temp_dir.path().join("tests/unit"))?; fs::create_dir_all(temp_dir.path().join("docs"))?; fs::write(temp_dir.path().join("src/main.rs"), "fn main() {}")?; fs::write( temp_dir.path().join("src/core/engine.rs"), "pub struct Engine {}", )?; fs::write( temp_dir.path().join("tests/unit/test_engine.rs"), "#[test] fn test() {}", )?; fs::write(temp_dir.path().join("README.md"), "# Project")?; Ok(temp_dir) } #[test] fn test_context_tracker_creation() { // Basic creation - where all great tests begin! let tracker = StContextTracker::new(); // Should create with empty context let context = tracker.analyze_context().unwrap(); match context { WorkContext::Exploring { depth, areas_visited, } => { assert_eq!(depth, 3, "Default exploration depth should be 3"); assert!(areas_visited.is_empty(), "No areas visited initially"); } _ => panic!("Should start in Exploring context"), } } #[test] fn test_record_operation() -> Result<()> { // Recording operations - the memory of our actions! let tracker = StContextTracker::new(); let op = create_test_operation("read", "/src/main.rs"); tracker.record_operation(op)?; // Should update context based on operation let context = tracker.analyze_context()?; assert!( matches!(context, WorkContext::Exploring { .. }), "Single read should keep exploring context" ); Ok(()) } #[test] #[ignore = "Hangs in CI - needs investigation"] fn test_context_detection_coding() -> Result<()> { // Detecting coding context - when the developer is in the zone! let tracker = StContextTracker::new(); // Simulate coding pattern: edits + tests tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("read", "/tests/test_main.rs"))?; let context = tracker.analyze_context()?; match context { WorkContext::Coding { language, focus_file, } => { assert_eq!(language, "rust", "Should detect Rust language"); assert_eq!( focus_file, PathBuf::from("/src/main.rs"), "Should identify focus file" ); } _ => panic!("Should detect Coding context"), } Ok(()) } #[test] fn test_context_detection_hunting() -> Result<()> { // Hunting context - when you're searching for that elusive bug! let tracker = StContextTracker::new(); // Simulate search pattern for i in 0..4 { tracker.record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: format!("search TODO file_{}.rs", i), path: PathBuf::from(format!("/src/file_{}.rs", i)), result_summary: "Found matches".to_string(), context_hints: vec![], })?; } let context = tracker.analyze_context()?; assert!( matches!(context, WorkContext::Hunting { .. }), "Multiple searches should trigger Hunting context" ); Ok(()) } #[test] fn test_context_detection_testing() -> Result<()> { // Testing context - where quality assurance happens! let tracker = StContextTracker::new(); // Simulate test-focused activity tracker.record_operation(create_test_operation("read", "/tests/test_main.rs"))?; tracker.record_operation(create_test_operation("edit", "/tests/test_utils.rs"))?; tracker.record_operation(create_test_operation("read", "/tests/test_core.rs"))?; let context = tracker.analyze_context()?; match context { WorkContext::Testing { test_files, .. } => { assert!(test_files.len() >= 2, "Should track multiple test files"); } _ => panic!("Should detect Testing context"), } Ok(()) } #[test] #[ignore = "Hangs in CI - needs investigation"] fn test_context_detection_exploring() -> Result<()> { // Exploring context - when you're getting familiar with the codebase! let tracker = StContextTracker::new(); // Simulate exploration pattern let files = vec![ "/src/main.rs", "/src/lib.rs", "/src/core/engine.rs", "/src/utils/helpers.rs", "/docs/README.md", ]; for file in files { tracker.record_operation(create_test_operation("read", file))?; } let context = tracker.analyze_context()?; match context { WorkContext::Exploring { depth, areas_visited, } => { assert_eq!(depth, 5, "Heavy reading should increase exploration depth"); assert!(!areas_visited.is_empty(), "Should track visited areas"); } _ => panic!("Should detect Exploring context"), } Ok(()) } #[test] fn test_suggestions_for_coding_context() -> Result<()> { // Suggestions during coding - your AI pair programmer! let tracker = StContextTracker::new(); // Set up coding context tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("read", "/tests/test_main.rs"))?; let suggestions = tracker.get_suggestions(Path::new("/src")); assert!( !suggestions.is_empty(), "Should provide suggestions for coding" ); assert!( suggestions .iter() .any(|s| s.contains("relations") || s.contains("test")), "Should suggest relevant commands" ); Ok(()) } #[test] fn test_suggestions_for_debugging_context() -> Result<()> { // Debugging suggestions - when you need all the help you can get! let tracker = StContextTracker::new(); // Manually set debugging context (since we can't easily trigger it) tracker.record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: "search error".to_string(), path: PathBuf::from("/src/main.rs"), result_summary: "Found errors".to_string(), context_hints: vec!["error".to_string()], })?; let suggestions = tracker.get_suggestions(Path::new("/src")); assert!( !suggestions.is_empty(), "Should provide debugging suggestions" ); Ok(()) } #[test] fn test_optimal_args_for_contexts() -> Result<()> { // Optimal arguments - because context should drive configuration! let tracker = StContextTracker::new(); // Test default context let args = tracker.get_optimal_args("st"); assert!( args.contains(&"--depth".to_string()), "Should include depth arg" ); // Set up coding context tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("read", "/tests/test_main.rs"))?; let args = tracker.get_optimal_args("st"); assert!( args.contains(&"ai".to_string()), "Coding context should use AI mode" ); Ok(()) } #[test] fn test_project_knowledge_tracking() -> Result<()> { // Project knowledge - learning from history! let tracker = StContextTracker::new(); // Access the same directory multiple times for _ in 0..5 { tracker.record_operation(create_test_operation("read", "/src/core/engine.rs"))?; } // Search for the same pattern multiple times for _ in 0..3 { tracker.record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: "search TODO".to_string(), path: PathBuf::from("/src"), result_summary: "Found".to_string(), context_hints: vec![], })?; } // Knowledge should be accumulated (we can't directly access it, but it affects suggestions) let suggestions = tracker.get_suggestions(Path::new("/")); assert!( !suggestions.is_empty(), "Should provide suggestions based on knowledge" ); Ok(()) } #[test] fn test_save_and_load_context() -> Result<()> { // Persistence - because context shouldn't be lost! let temp_dir = create_test_project()?; let tracker = StContextTracker::new(); // Build up some context tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("search TODO", "/src"))?; // Save context tracker.save_context(temp_dir.path())?; // Verify file exists let context_file = temp_dir.path().join(".st_context.json"); assert!(context_file.exists(), "Context file should be created"); // Create new tracker and load let new_tracker = StContextTracker::new(); new_tracker.load_context(temp_dir.path())?; // Should have restored context let context = new_tracker.analyze_context()?; assert!( !matches!(context, WorkContext::Exploring { depth: 3, .. }), "Should have loaded non-default context" ); Ok(()) } #[test] fn test_language_detection() -> Result<()> { // Language detection - polyglot testing! let tracker = StContextTracker::new(); let test_cases = vec![ ("test.rs", "rust"), ("test.py", "python"), ("test.js", "javascript"), ("test.jsx", "javascript"), ("test.ts", "typescript"), ("test.tsx", "typescript"), ("test.go", "go"), ("test.java", "java"), ("test.cpp", "cpp"), ("test.c", "c"), ("test.unknown", "unknown"), ]; for (filename, expected_lang) in test_cases { tracker.record_operation(create_test_operation("edit", filename))?; tracker.record_operation(create_test_operation("edit", filename))?; tracker.record_operation(create_test_operation("read", "/tests/test.rs"))?; let context = tracker.analyze_context()?; if let WorkContext::Coding { language, .. } = context { assert_eq!( language, expected_lang, "Should detect {} for {}", expected_lang, filename ); } } Ok(()) } #[test] fn test_operation_history_limit() -> Result<()> { // History limits - because infinite memory is a myth! let tracker = StContextTracker::new(); // Add more than 50 operations for i in 0..60 { tracker.record_operation(create_test_operation("read", &format!("/file{}.rs", i)))?; } // History should be capped at 50 (we can't check directly, but it shouldn't crash) let context = tracker.analyze_context()?; assert!( matches!(context, WorkContext::Exploring { .. }), "Should still analyze context with full history" ); Ok(()) } #[test] fn test_contextual_command_builder() -> Result<()> { // Command builder - context-aware command construction! let tracker = Arc::new(StContextTracker::new()); let cmd_builder = ContextualStCommand::new(Arc::clone(&tracker)); // Set up a coding context tracker.record_operation(create_test_operation("edit", "/src/main.rs"))?; tracker.record_operation(create_test_operation("edit", "/src/lib.rs"))?; tracker.record_operation(create_test_operation("read", "/tests/test.rs"))?; let args = cmd_builder.build("analyze"); assert!(!args.is_empty(), "Should build context-aware arguments"); Ok(()) } #[test] fn test_empty_project_knowledge() { // Empty knowledge - starting from scratch! let knowledge = ProjectKnowledge::default(); assert!( knowledge.key_files.is_empty(), "Should start with no key files" ); assert!( knowledge.common_searches.is_empty(), "Should start with no searches" ); assert!( knowledge.hot_directories.is_empty(), "Should start with no hot directories" ); assert!( knowledge.build_commands.is_empty(), "Should start with no build commands" ); assert!( knowledge.test_patterns.is_empty(), "Should start with no test patterns" ); assert!( knowledge.doc_locations.is_empty(), "Should start with no doc locations" ); } #[test] #[ignore = "Hangs in CI - needs investigation"] fn test_concurrent_context_updates() -> Result<()> { // Concurrency - when multiple threads want to update context! use std::thread; let tracker = Arc::new(StContextTracker::new()); let mut handles = vec![]; // Spawn threads that record operations for i in 0..10 { let tracker = Arc::clone(&tracker); let handle = thread::spawn(move || { tracker.record_operation(create_test_operation("read", &format!("/file{}.rs", i))) }); handles.push(handle); } // All should complete without deadlock for handle in handles { handle.join().unwrap()?; } // Context should be updated let context = tracker.analyze_context()?; assert!( matches!(context, WorkContext::Exploring { .. }), "Should have valid context after concurrent updates" ); Ok(()) } #[test] fn test_work_context_serialization() -> Result<()> { // Serialization - because contexts need to persist! let contexts = vec![ WorkContext::Coding { language: "rust".to_string(), focus_file: PathBuf::from("/src/main.rs"), }, WorkContext::Debugging { error_pattern: "panic".to_string(), files: vec![PathBuf::from("/src/lib.rs")], }, WorkContext::Testing { test_files: vec![PathBuf::from("/tests/test.rs")], target_files: vec![PathBuf::from("/src/main.rs")], }, WorkContext::Exploring { depth: 5, areas_visited: vec![PathBuf::from("/src")], }, ]; for context in contexts { let serialized = serde_json::to_string(&context)?; let deserialized: WorkContext = serde_json::from_str(&serialized)?; assert_eq!( context, deserialized, "Context should survive serialization" ); } Ok(()) } #[test] fn test_suggestions_with_hot_directories() -> Result<()> { // Hot directory suggestions - frequently visited paths! let tracker = StContextTracker::new(); // Make /src/core "hot" by accessing it frequently for _ in 0..10 { tracker.record_operation(create_test_operation("read", "/src/core/engine.rs"))?; } // Access other areas less tracker.record_operation(create_test_operation("read", "/tests/test.rs"))?; tracker.record_operation(create_test_operation("read", "/docs/README.md"))?; let suggestions = tracker.get_suggestions(Path::new("/")); // Should suggest checking hot areas (implementation specific) assert!(!suggestions.is_empty(), "Should provide suggestions"); Ok(()) } #[test] fn test_edge_case_empty_operation() -> Result<()> { // Empty operations - when nothing happens, but we still track it! let tracker = StContextTracker::new(); let empty_op = ContextualOperation { timestamp: SystemTime::now(), operation: String::new(), path: PathBuf::new(), result_summary: String::new(), context_hints: vec![], }; // Should handle gracefully tracker.record_operation(empty_op)?; let context = tracker.analyze_context()?; assert!( matches!(context, WorkContext::Exploring { .. }), "Empty operations shouldn't break context analysis" ); Ok(()) } #[test] fn test_load_from_nonexistent_file() -> Result<()> { // Loading from nowhere - graceful degradation! let tracker = StContextTracker::new(); let temp_dir = TempDir::new()?; // Should handle missing file gracefully let result = tracker.load_context(temp_dir.path()); assert!( result.is_ok(), "Should handle missing context file gracefully" ); Ok(()) } #[test] fn test_all_work_context_types() -> Result<()> { // Every context type needs love! let all_contexts = vec![ WorkContext::Coding { language: "rust".to_string(), focus_file: PathBuf::from("main.rs"), }, WorkContext::Debugging { error_pattern: "error".to_string(), files: vec![], }, WorkContext::Refactoring { pattern: "rename".to_string(), scope: PathBuf::from("src"), }, WorkContext::Exploring { depth: 3, areas_visited: vec![], }, WorkContext::Testing { test_files: vec![], target_files: vec![], }, WorkContext::Documenting { doc_type: "api".to_string(), target: PathBuf::from("docs"), }, WorkContext::Optimizing { metrics: vec!["speed".to_string()], hotspots: vec![], }, WorkContext::Hunting { query: "bug".to_string(), found_locations: vec![], }, WorkContext::Building { build_system: "cargo".to_string(), targets: vec!["release".to_string()], }, WorkContext::VersionControl { operation: "commit".to_string(), changed_files: vec![], }, ]; let tracker = StContextTracker::new(); for context in all_contexts { // Each context type should produce appropriate suggestions let suggestions = tracker.get_suggestions(Path::new("/")); assert!( !suggestions.is_empty(), "Should provide suggestions for context: {:?}", context ); } Ok(()) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/8b-is/smart-tree'

If you have feedback or need assistance with the MCP directory API, please join our Discord server