use std::path::{Path, PathBuf};
use std::sync::Arc;
use thread_flow::incremental::analyzer::IncrementalAnalyzer;
use thread_flow::incremental::dependency_builder::DependencyGraphBuilder;
use thread_flow::incremental::storage::InMemoryStorage;
use thread_flow::incremental::types::{DependencyEdge, DependencyType};
use tokio::fs;
use tokio::io::AsyncWriteExt;
struct IntegrationFixture {
temp_dir: tempfile::TempDir,
analyzer: IncrementalAnalyzer,
builder: DependencyGraphBuilder,
}
impl IntegrationFixture {
async fn new() -> Self {
let temp_dir = tempfile::tempdir().expect("create temp dir");
let analyzer_storage = InMemoryStorage::new();
let analyzer = IncrementalAnalyzer::new(Box::new(analyzer_storage));
let builder_storage = InMemoryStorage::new();
let builder = DependencyGraphBuilder::new(Box::new(builder_storage));
Self {
temp_dir,
analyzer,
builder,
}
}
fn temp_path(&self) -> &Path {
self.temp_dir.path()
}
async fn create_file(&self, relative_path: &str, content: &str) -> PathBuf {
let file_path = self.temp_path().join(relative_path);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent).await.expect("create parent dir");
}
let mut file = fs::File::create(&file_path).await.expect("create file");
file.write_all(content.as_bytes())
.await
.expect("write file");
file_path
}
async fn update_file(&self, file_path: &Path, content: &str) {
let mut file = fs::File::create(file_path).await.expect("open file");
file.write_all(content.as_bytes())
.await
.expect("write file");
}
async fn delete_file(&self, file_path: &Path) {
fs::remove_file(file_path).await.expect("delete file");
}
async fn analyze_and_extract(
&mut self,
paths: &[PathBuf],
) -> thread_flow::incremental::analyzer::AnalysisResult {
let result = self
.analyzer
.analyze_changes(paths)
.await
.expect("analyze changes");
self.builder
.extract_files(paths)
.await
.expect("extract dependencies");
let builder_graph = self.builder.graph();
let analyzer_graph = self.analyzer.graph_mut();
for edge in &builder_graph.edges {
analyzer_graph.add_edge(edge.clone());
}
result
}
async fn assert_fingerprint_count(&self, expected: usize) {
let graph = self.builder.graph();
assert_eq!(
graph.node_count(),
expected,
"Expected {} fingerprints, found {}",
expected,
graph.node_count()
);
}
async fn assert_edge_count(&self, expected: usize) {
let graph = self.builder.graph();
assert_eq!(
graph.edge_count(),
expected,
"Expected {} edges, found {}",
expected,
graph.edge_count()
);
}
}
#[tokio::test]
async fn test_e2e_empty_project_initialization() {
let fixture = IntegrationFixture::new().await;
fixture.assert_fingerprint_count(0).await;
fixture.assert_edge_count(0).await;
}
#[tokio::test]
async fn test_e2e_single_file_analysis() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture
.create_file("main.rs", "fn main() { println!(\"Hello\"); }")
.await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.assert_fingerprint_count(1).await;
fixture.assert_edge_count(0).await; }
#[tokio::test]
async fn test_e2e_small_batch_updates() {
let mut fixture = IntegrationFixture::new().await;
let file1 = fixture.create_file("a.rs", "// File A").await;
let file2 = fixture.create_file("b.rs", "// File B").await;
let file3 = fixture.create_file("c.rs", "// File C").await;
let result = fixture
.analyze_and_extract(&[file1.clone(), file2.clone(), file3.clone()])
.await;
assert_eq!(result.changed_files.len(), 3);
assert_eq!(result.cache_hit_rate, 0.0);
let result = fixture
.analyze_and_extract(&[file1.clone(), file2.clone(), file3.clone()])
.await;
assert_eq!(result.changed_files.len(), 0);
assert_eq!(result.cache_hit_rate, 1.0); }
#[tokio::test]
async fn test_e2e_cache_hit_validation() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("test.rs", "const X: u32 = 42;").await;
let result1 = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(result1.changed_files.len(), 1);
assert_eq!(result1.cache_hit_rate, 0.0);
let result2 = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(result2.changed_files.len(), 0);
assert_eq!(result2.cache_hit_rate, 1.0);
let result3 = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(result3.changed_files.len(), 0);
assert_eq!(result3.cache_hit_rate, 1.0);
}
#[tokio::test]
async fn test_e2e_full_reanalysis_trigger() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("data.rs", "const X: i32 = 10;").await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture
.update_file(&file, "const X: i32 = 20; // Changed")
.await;
let result = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(result.changed_files.len(), 1);
assert_eq!(result.cache_hit_rate, 0.0); }
#[tokio::test]
async fn test_e2e_project_reset() {
let mut fixture = IntegrationFixture::new().await;
let file1 = fixture.create_file("a.rs", "// A").await;
let file2 = fixture.create_file("b.rs", "// B").await;
fixture
.analyze_and_extract(&[file1.clone(), file2.clone()])
.await;
fixture.assert_fingerprint_count(2).await;
fixture.analyzer.graph_mut().clear();
fixture.builder = DependencyGraphBuilder::new(Box::new(InMemoryStorage::new()));
fixture.analyzer.persist().await.expect("persist");
fixture.assert_fingerprint_count(0).await;
fixture.assert_edge_count(0).await;
}
#[tokio::test]
async fn test_e2e_multi_file_updates() {
let mut fixture = IntegrationFixture::new().await;
let files: Vec<PathBuf> = (0..5)
.map(|i| {
futures::executor::block_on(
fixture.create_file(&format!("file{}.rs", i), &format!("// File {}", i)),
)
})
.collect();
let result = fixture
.analyzer
.analyze_changes(&files)
.await
.expect("analyze");
assert_eq!(result.changed_files.len(), 5);
fixture.update_file(&files[1], "// File 1 updated").await;
fixture.update_file(&files[3], "// File 3 updated").await;
let result = fixture
.analyzer
.analyze_changes(&files)
.await
.expect("analyze");
assert_eq!(result.changed_files.len(), 2);
assert_eq!(result.cache_hit_rate, 0.6); }
#[tokio::test]
async fn test_e2e_incremental_vs_full_comparison() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("compare.rs", "fn test() {}").await;
let full_result = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(full_result.changed_files.len(), 1);
let incremental_result = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(incremental_result.changed_files.len(), 0);
assert!(incremental_result.cache_hit_rate > full_result.cache_hit_rate);
}
#[tokio::test]
async fn test_e2e_rust_cross_file_deps() {
let mut fixture = IntegrationFixture::new().await;
let lib = fixture.create_file("lib.rs", "pub fn helper() {}").await;
let main = fixture
.create_file("main.rs", "mod lib; fn main() { lib::helper(); }")
.await;
fixture
.analyze_and_extract(&[lib.clone(), main.clone()])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(std::slice::from_ref(&lib))
.await
.expect("invalidate");
assert!(affected.contains(&main));
}
#[tokio::test]
async fn test_e2e_rust_mod_declarations() {
let mut fixture = IntegrationFixture::new().await;
let utils = fixture.create_file("utils.rs", "pub fn util() {}").await;
let main = fixture
.create_file("main.rs", "mod utils; fn main() {}")
.await;
fixture
.analyze_and_extract(&[utils.clone(), main.clone()])
.await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_typescript_esm_imports() {
let mut fixture = IntegrationFixture::new().await;
let utils = fixture
.create_file("utils.ts", "export const helper = () => {};")
.await;
let main = fixture
.create_file("main.ts", "import { helper } from './utils';")
.await;
fixture.analyze_and_extract(&[utils, main]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_typescript_exports() {
let mut fixture = IntegrationFixture::new().await;
let types = fixture
.create_file("types.ts", "export interface User { name: string; }")
.await;
let app = fixture
.create_file("app.ts", "import type { User } from './types';")
.await;
fixture.analyze_and_extract(&[types, app]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_typescript_namespace() {
let mut fixture = IntegrationFixture::new().await;
let ns = fixture
.create_file(
"namespace.ts",
"export namespace Utils { export const x = 1; }",
)
.await;
let consumer = fixture
.create_file("consumer.ts", "import { Utils } from './namespace';")
.await;
fixture.analyze_and_extract(&[ns, consumer]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_python_import_chains() {
let mut fixture = IntegrationFixture::new().await;
let base = fixture
.create_file("base.py", "def base_func(): pass")
.await;
let mid = fixture
.create_file("mid.py", "from base import base_func")
.await;
let top = fixture
.create_file("top.py", "from mid import base_func")
.await;
fixture
.analyze_and_extract(&[base.clone(), mid.clone(), top.clone()])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(&[base])
.await
.expect("invalidate");
assert!(!affected.is_empty()); }
#[tokio::test]
async fn test_e2e_python_package_imports() {
let mut fixture = IntegrationFixture::new().await;
let init = fixture
.create_file("pkg/__init__.py", "from .module import func")
.await;
let module = fixture
.create_file("pkg/module.py", "def func(): pass")
.await;
fixture.analyze_and_extract(&[init, module]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_go_package_imports() {
let mut fixture = IntegrationFixture::new().await;
let util = fixture
.create_file("util/util.go", "package util\nfunc Helper() {}")
.await;
let main = fixture
.create_file("main.go", "package main\nimport \"./util\"\nfunc main() {}")
.await;
fixture.analyze_and_extract(&[util, main]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_go_internal_references() {
let mut fixture = IntegrationFixture::new().await;
let internal = fixture
.create_file("internal/helper.go", "package internal\nfunc Help() {}")
.await;
let pkg = fixture
.create_file("pkg/pkg.go", "package pkg\nimport \"../internal\"")
.await;
fixture.analyze_and_extract(&[internal, pkg]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_language_mix_validation() {
let mut fixture = IntegrationFixture::new().await;
let rust = fixture
.create_file("src/lib.rs", "pub fn rust_func() {}")
.await;
let ts = fixture
.create_file("src/app.ts", "export const tsFunc = () => {};")
.await;
let py = fixture
.create_file("scripts/helper.py", "def py_func(): pass")
.await;
let go_file = fixture
.create_file("cmd/main.go", "package main\nfunc main() {}")
.await;
fixture.analyze_and_extract(&[rust, ts, py, go_file]).await;
fixture.assert_fingerprint_count(4).await;
}
#[tokio::test]
async fn test_e2e_multi_language_dependency_isolation() {
let mut fixture = IntegrationFixture::new().await;
let rust1 = fixture.create_file("a.rs", "fn a() {}").await;
let rust2 = fixture.create_file("b.rs", "fn b() {}").await;
let ts1 = fixture.create_file("x.ts", "const x = 1;").await;
let ts2 = fixture.create_file("y.ts", "const y = 2;").await;
fixture
.analyze_and_extract(&[rust1.clone(), rust2, ts1, ts2])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(&[rust1])
.await
.expect("invalidate");
assert_eq!(affected.len(), 1); }
#[tokio::test]
async fn test_e2e_javascript_vs_typescript() {
let mut fixture = IntegrationFixture::new().await;
let js = fixture.create_file("app.js", "const x = 42;").await;
let ts = fixture.create_file("app.ts", "const y: number = 42;").await;
fixture.analyze_and_extract(&[js, ts]).await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_linear_dependency_chain() {
let mut fixture = IntegrationFixture::new().await;
let d = fixture.create_file("d.rs", "pub fn d() {}").await;
let c = fixture
.create_file("c.rs", "mod d; pub fn c() { d::d(); }")
.await;
let b = fixture
.create_file("b.rs", "mod c; pub fn b() { c::c(); }")
.await;
let a = fixture
.create_file("a.rs", "mod b; fn main() { b::b(); }")
.await;
fixture
.analyze_and_extract(&[a.clone(), b.clone(), c.clone(), d.clone()])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(std::slice::from_ref(&d))
.await
.expect("invalidate");
assert!(affected.contains(&d));
}
#[tokio::test]
async fn test_e2e_tree_dependencies() {
let mut fixture = IntegrationFixture::new().await;
let d = fixture.create_file("d.rs", "pub fn d() {}").await;
let b = fixture.create_file("b.rs", "pub fn b() {}").await;
let c = fixture.create_file("c.rs", "pub fn c() {}").await;
let a = fixture.create_file("a.rs", "fn main() {}").await;
fixture.analyze_and_extract(&[a, b, c, d.clone()]).await;
let affected = fixture
.analyzer
.invalidate_dependents(&[d])
.await
.expect("invalidate");
assert!(!affected.is_empty());
}
#[tokio::test]
async fn test_e2e_diamond_dependencies() {
let mut fixture = IntegrationFixture::new().await;
let d = fixture.create_file("d.rs", "pub fn d() {}").await;
let c = fixture.create_file("c.rs", "pub fn c() {}").await;
let b = fixture.create_file("b.rs", "pub fn b() {}").await;
let a = fixture.create_file("a.rs", "fn main() {}").await;
fixture
.analyze_and_extract(&[a.clone(), b, c, d.clone()])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(std::slice::from_ref(&d))
.await
.expect("invalidate");
assert!(affected.contains(&d));
}
#[tokio::test]
async fn test_e2e_circular_detection() {
let mut fixture = IntegrationFixture::new().await;
let a = fixture.create_file("a.rs", "// Circular A").await;
let b = fixture.create_file("b.rs", "// Circular B").await;
let edge_a_to_b = DependencyEdge::new(a.clone(), b.clone(), DependencyType::Import);
let edge_b_to_a = DependencyEdge::new(b.clone(), a.clone(), DependencyType::Import);
fixture.analyzer.graph_mut().add_edge(edge_a_to_b);
fixture.analyzer.graph_mut().add_edge(edge_b_to_a);
let files: thread_utilities::RapidSet<PathBuf> = [a.clone(), b.clone()].into_iter().collect();
let result = fixture.analyzer.graph().topological_sort(&files);
assert!(result.is_err(), "Should detect circular dependency");
}
#[tokio::test]
async fn test_e2e_symbol_level_tracking() {
let mut fixture = IntegrationFixture::new().await;
let types = fixture
.create_file("types.rs", "pub struct User { name: String }")
.await;
let handler = fixture.create_file("handler.rs", "use types::User;").await;
fixture
.analyze_and_extract(&[types.clone(), handler.clone()])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(&[types])
.await
.expect("invalidate");
assert!(!affected.is_empty());
}
#[tokio::test]
async fn test_e2e_reexport_chains() {
let mut fixture = IntegrationFixture::new().await;
let core = fixture.create_file("core.rs", "pub fn core_fn() {}").await;
let lib = fixture
.create_file("lib.rs", "pub use core::core_fn;")
.await;
let public = fixture.create_file("public.rs", "use lib::core_fn;").await;
fixture
.analyze_and_extract(&[core.clone(), lib, public])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(&[core])
.await
.expect("invalidate");
assert!(!affected.is_empty());
}
#[tokio::test]
async fn test_e2e_weak_vs_strong_dependencies() {
let mut fixture = IntegrationFixture::new().await;
let strong_dep = fixture.create_file("strong.rs", "pub fn strong() {}").await;
let strong_user = fixture
.create_file("use_strong.rs", "use strong::strong;")
.await;
let weak_dep = fixture.create_file("weak.rs", "fn weak() {}").await;
fixture
.analyze_and_extract(&[strong_dep.clone(), strong_user, weak_dep.clone()])
.await;
let strong_affected = fixture
.analyzer
.invalidate_dependents(&[strong_dep])
.await
.expect("invalidate");
assert!(!strong_affected.is_empty());
let weak_affected = fixture
.analyzer
.invalidate_dependents(&[weak_dep])
.await
.expect("invalidate");
assert_eq!(weak_affected.len(), 1); }
#[tokio::test]
async fn test_e2e_partial_dependency_updates() {
let mut fixture = IntegrationFixture::new().await;
let base = fixture.create_file("base.rs", "pub fn base() {}").await;
let mid1 = fixture.create_file("mid1.rs", "use base::base;").await;
let mid2 = fixture.create_file("mid2.rs", "// Independent").await;
let top = fixture.create_file("top.rs", "// Independent").await;
fixture
.analyze_and_extract(&[base.clone(), mid1.clone(), mid2, top])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(std::slice::from_ref(&base))
.await
.expect("invalidate");
assert!(affected.contains(&base));
}
#[tokio::test]
async fn test_e2e_transitive_closure() {
let mut fixture = IntegrationFixture::new().await;
let e = fixture.create_file("e.rs", "pub fn e() {}").await;
let d = fixture.create_file("d.rs", "pub fn d() {}").await;
let c = fixture.create_file("c.rs", "pub fn c() {}").await;
let b = fixture.create_file("b.rs", "pub fn b() {}").await;
let a = fixture.create_file("a.rs", "fn main() {}").await;
fixture.analyze_and_extract(&[a, b, c, d, e.clone()]).await;
let affected = fixture
.analyzer
.invalidate_dependents(std::slice::from_ref(&e))
.await
.expect("invalidate");
assert!(affected.contains(&e));
}
#[tokio::test]
async fn test_e2e_dependency_graph_visualization() {
let mut fixture = IntegrationFixture::new().await;
let file1 = fixture.create_file("file1.rs", "pub fn f1() {}").await;
let file2 = fixture
.create_file("file2.rs", "use crate::file1;\npub fn f2() {}")
.await;
let file3 = fixture
.create_file("file3.rs", "use crate::file2;\npub fn f3() {}")
.await;
fixture
.analyze_and_extract(&[file1.clone(), file2.clone(), file3.clone()])
.await;
let graph = fixture.builder.graph();
assert!(
graph.node_count() >= 3,
"Expected at least 3 nodes in dependency graph"
);
assert!(
graph.edge_count() >= 2,
"Expected at least 2 edges in dependency graph"
);
}
#[cfg(feature = "parallel")]
#[tokio::test]
async fn test_e2e_parallel_rayon_analysis() {
let mut fixture = IntegrationFixture::new().await;
let files: Vec<PathBuf> = (0..10)
.map(|i| {
futures::executor::block_on(
fixture.create_file(&format!("parallel{}.rs", i), &format!("// File {}", i)),
)
})
.collect();
let result = fixture
.analyzer
.analyze_changes(&files)
.await
.expect("analyze");
assert_eq!(result.changed_files.len(), 10);
}
#[tokio::test]
async fn test_e2e_parallel_tokio_analysis() {
let mut fixture = IntegrationFixture::new().await;
let files: Vec<PathBuf> = (0..10)
.map(|i| {
futures::executor::block_on(
fixture.create_file(&format!("async{}.rs", i), &format!("// Async {}", i)),
)
})
.collect();
let result = fixture
.analyzer
.analyze_changes(&files)
.await
.expect("analyze");
assert_eq!(result.changed_files.len(), 10);
}
#[tokio::test]
async fn test_e2e_thread_safety_validation() {
let fixture = Arc::new(tokio::sync::Mutex::new(IntegrationFixture::new().await));
let file1 = {
let fixture = fixture.lock().await;
fixture.create_file("thread1.rs", "// Thread 1").await
};
let file2 = {
let fixture = fixture.lock().await;
fixture.create_file("thread2.rs", "// Thread 2").await
};
let handle1 = {
let fixture = Arc::clone(&fixture);
let file = file1.clone();
tokio::spawn(async move {
let mut fixture = fixture.lock().await;
fixture.analyzer.analyze_changes(&[file]).await
})
};
let handle2 = {
let fixture = Arc::clone(&fixture);
let file = file2.clone();
tokio::spawn(async move {
let mut fixture = fixture.lock().await;
fixture.analyzer.analyze_changes(&[file]).await
})
};
let result1 = handle1.await.expect("join").expect("analyze");
let result2 = handle2.await.expect("join").expect("analyze");
assert_eq!(result1.changed_files.len(), 1);
assert_eq!(result2.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_race_condition_prevention() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("race.rs", "// Initial").await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.update_file(&file, "// Modified").await;
let result = fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
assert_eq!(result.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_deadlock_prevention() {
let mut fixture = IntegrationFixture::new().await;
let file1 = fixture.create_file("lock1.rs", "// Lock 1").await;
let file2 = fixture.create_file("lock2.rs", "// Lock 2").await;
let result = fixture.analyze_and_extract(&[file1, file2]).await;
assert_eq!(result.changed_files.len(), 2);
}
#[cfg(feature = "parallel")]
#[tokio::test]
async fn test_e2e_feature_gating_rayon() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("rayon_test.rs", "// Rayon").await;
let result = fixture.analyze_and_extract(&[file]).await;
assert_eq!(result.changed_files.len(), 1);
}
#[cfg(not(feature = "parallel"))]
#[tokio::test]
async fn test_e2e_feature_gating_tokio_fallback() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("tokio_test.rs", "// Tokio").await;
let result = fixture.analyze_and_extract(&[file]).await;
assert_eq!(result.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_concurrent_invalidation() {
let mut fixture = IntegrationFixture::new().await;
let base = fixture.create_file("base.rs", "pub fn base() {}").await;
let dep1 = fixture.create_file("dep1.rs", "use base::base;").await;
let dep2 = fixture.create_file("dep2.rs", "use base::base;").await;
fixture
.analyze_and_extract(&[base.clone(), dep1, dep2])
.await;
let affected = fixture
.analyzer
.invalidate_dependents(&[base])
.await
.expect("invalidate");
assert!(!affected.is_empty());
}
#[tokio::test]
async fn test_e2e_inmemory_persistence() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("persist.rs", "fn persist() {}").await;
fixture.analyze_and_extract(&[file]).await;
fixture.analyzer.persist().await.expect("persist");
fixture.assert_fingerprint_count(1).await;
}
#[tokio::test]
async fn test_e2e_state_transitions() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("state.rs", "// State 1").await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.assert_fingerprint_count(1).await;
fixture.update_file(&file, "// State 2").await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.assert_fingerprint_count(1).await;
fixture.delete_file(&file).await;
}
#[tokio::test]
async fn test_e2e_error_recovery() {
let mut fixture = IntegrationFixture::new().await;
let valid = fixture.create_file("valid.rs", "fn valid() {}").await;
fixture.analyze_and_extract(&[valid]).await;
fixture.assert_fingerprint_count(1).await;
}
#[tokio::test]
async fn test_e2e_concurrent_access() {
let fixture = Arc::new(tokio::sync::Mutex::new(IntegrationFixture::new().await));
let file1 = {
let fixture = fixture.lock().await;
fixture.create_file("concurrent1.rs", "// File 1").await
};
let file2 = {
let fixture = fixture.lock().await;
fixture.create_file("concurrent2.rs", "// File 2").await
};
let handle1 = {
let fixture = Arc::clone(&fixture);
let file = file1.clone();
tokio::spawn(async move {
let mut fixture = fixture.lock().await;
fixture.analyze_and_extract(&[file]).await
})
};
let handle2 = {
let fixture = Arc::clone(&fixture);
let file = file2.clone();
tokio::spawn(async move {
let mut fixture = fixture.lock().await;
fixture.analyze_and_extract(&[file]).await
})
};
handle1.await.expect("join");
handle2.await.expect("join");
let fixture = fixture.lock().await;
fixture.assert_fingerprint_count(2).await;
}
#[tokio::test]
async fn test_e2e_storage_consistency() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("consistency.rs", "// Consistent").await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.assert_fingerprint_count(1).await;
}
#[tokio::test]
async fn test_e2e_storage_isolation() {
let mut fixture1 = IntegrationFixture::new().await;
let mut fixture2 = IntegrationFixture::new().await;
let file1 = fixture1.create_file("isolated1.rs", "// Isolated 1").await;
let file2 = fixture2.create_file("isolated2.rs", "// Isolated 2").await;
fixture1.analyze_and_extract(&[file1]).await;
fixture2.analyze_and_extract(&[file2]).await;
fixture1.assert_fingerprint_count(1).await;
fixture2.assert_fingerprint_count(1).await;
}
#[tokio::test]
async fn test_e2e_parse_failures() {
let mut fixture = IntegrationFixture::new().await;
let invalid = fixture
.create_file("invalid.rs", "fn main( { incomplete")
.await;
let result = fixture.analyzer.analyze_changes(&[invalid]).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_e2e_invalid_utf8() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture.create_file("utf8.rs", "// Valid UTF-8: ✓").await;
let result = fixture.analyzer.analyze_changes(&[file]).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_e2e_large_files() {
let mut fixture = IntegrationFixture::new().await;
let large_content: String = (0..10000)
.map(|i| format!("const VAR{}: u32 = {};\n", i, i))
.collect();
let large_file = fixture.create_file("large.rs", &large_content).await;
let result = fixture.analyze_and_extract(&[large_file]).await;
assert_eq!(result.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_empty_files() {
let mut fixture = IntegrationFixture::new().await;
let empty = fixture.create_file("empty.rs", "").await;
let result = fixture.analyze_and_extract(&[empty]).await;
assert_eq!(result.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_concurrent_modifications() {
let mut fixture = IntegrationFixture::new().await;
let file = fixture
.create_file("concurrent_mod.rs", "// Version 1")
.await;
fixture
.analyze_and_extract(std::slice::from_ref(&file))
.await;
fixture.update_file(&file, "// Version 2").await;
let result = fixture.analyze_and_extract(&[file]).await;
assert_eq!(result.changed_files.len(), 1);
}
#[tokio::test]
async fn test_e2e_nonexistent_file_handling() {
let mut fixture = IntegrationFixture::new().await;
let nonexistent = fixture.temp_path().join("nonexistent.rs");
let result = fixture.analyzer.analyze_changes(&[nonexistent]).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_e2e_comprehensive_summary() {
let mut fixture = IntegrationFixture::new().await;
let rust = fixture.create_file("src/main.rs", "fn main() {}").await;
let ts = fixture.create_file("web/app.ts", "const x = 1;").await;
let result = fixture
.analyze_and_extract(&[rust.clone(), ts.clone()])
.await;
assert_eq!(result.changed_files.len(), 2);
assert_eq!(result.cache_hit_rate, 0.0);
let result = fixture
.analyze_and_extract(&[rust.clone(), ts.clone()])
.await;
assert_eq!(result.changed_files.len(), 0);
assert_eq!(result.cache_hit_rate, 1.0);
fixture
.update_file(&rust, "fn main() { println!(\"Updated\"); }")
.await;
let result = fixture.analyze_and_extract(&[rust, ts]).await;
assert_eq!(result.changed_files.len(), 1);
assert_eq!(result.cache_hit_rate, 0.5);
fixture.assert_fingerprint_count(2).await;
println!("✓ All E2E integration tests completed successfully");
}