use recoco::base::value::{BasicValue, FieldValues, ScopeValue, Value};
use std::path::PathBuf;
use thread_ast_engine::tree_sitter::LanguageExt;
use thread_flow::conversion::serialize_parsed_doc;
use thread_language::{Python, Rust, SupportLang, Tsx};
use thread_services::conversion::{compute_content_fingerprint, extract_basic_metadata};
use thread_services::types::{ParsedDocument, SymbolInfo, SymbolKind, Visibility};
fn create_rust_document(
content: &str,
) -> ParsedDocument<thread_ast_engine::tree_sitter::StrDoc<Rust>> {
let ast_root = Rust.ast_grep(content);
let fingerprint = compute_content_fingerprint(content);
ParsedDocument::new(
ast_root,
PathBuf::from("test.rs"),
SupportLang::Rust,
fingerprint,
)
}
fn create_python_document(
content: &str,
) -> ParsedDocument<thread_ast_engine::tree_sitter::StrDoc<Python>> {
let ast_root = Python.ast_grep(content);
let fingerprint = compute_content_fingerprint(content);
ParsedDocument::new(
ast_root,
PathBuf::from("test.py"),
SupportLang::Python,
fingerprint,
)
}
fn create_typescript_document(
content: &str,
) -> ParsedDocument<thread_ast_engine::tree_sitter::StrDoc<Tsx>> {
let ast_root = Tsx.ast_grep(content);
let fingerprint = compute_content_fingerprint(content);
ParsedDocument::new(
ast_root,
PathBuf::from("test.ts"),
SupportLang::TypeScript,
fingerprint,
)
}
fn extract_symbol_count(value: &Value) -> usize {
match value {
Value::Struct(FieldValues { fields }) => match &fields[0] {
Value::LTable(symbols) => symbols.len(),
_ => panic!("Expected LTable for symbols"),
},
_ => panic!("Expected Struct output"),
}
}
fn extract_import_count(value: &Value) -> usize {
match value {
Value::Struct(FieldValues { fields }) => match &fields[1] {
Value::LTable(imports) => imports.len(),
_ => panic!("Expected LTable for imports"),
},
_ => panic!("Expected Struct output"),
}
}
fn extract_call_count(value: &Value) -> usize {
match value {
Value::Struct(FieldValues { fields }) => match &fields[2] {
Value::LTable(calls) => calls.len(),
_ => panic!("Expected LTable for calls"),
},
_ => panic!("Expected Struct output"),
}
}
fn extract_fingerprint(value: &Value) -> Vec<u8> {
match value {
Value::Struct(FieldValues { fields }) => match &fields[3] {
Value::Basic(BasicValue::Bytes(bytes)) => bytes.to_vec(),
_ => panic!("Expected Bytes for fingerprint"),
},
_ => panic!("Expected Struct output"),
}
}
fn validate_symbol_structure(symbol: &ScopeValue) {
let ScopeValue(FieldValues { fields }) = symbol;
assert_eq!(
fields.len(),
3,
"Symbol should have 3 fields: name, kind, scope"
);
assert!(
matches!(&fields[0], Value::Basic(BasicValue::Str(_))),
"Name should be string"
);
assert!(
matches!(&fields[1], Value::Basic(BasicValue::Str(_))),
"Kind should be string"
);
assert!(
matches!(&fields[2], Value::Basic(BasicValue::Str(_))),
"Scope should be string"
);
}
fn validate_import_structure(import: &ScopeValue) {
let ScopeValue(FieldValues { fields }) = import;
assert_eq!(
fields.len(),
3,
"Import should have 3 fields: symbol_name, source_path, kind"
);
assert!(
matches!(&fields[0], Value::Basic(BasicValue::Str(_))),
"Symbol name should be string"
);
assert!(
matches!(&fields[1], Value::Basic(BasicValue::Str(_))),
"Source path should be string"
);
assert!(
matches!(&fields[2], Value::Basic(BasicValue::Str(_))),
"Kind should be string"
);
}
fn validate_call_structure(call: &ScopeValue) {
let ScopeValue(FieldValues { fields }) = call;
assert_eq!(
fields.len(),
2,
"Call should have 2 fields: function_name, arguments_count"
);
assert!(
matches!(&fields[0], Value::Basic(BasicValue::Str(_))),
"Function name should be string"
);
assert!(
matches!(&fields[1], Value::Basic(BasicValue::Int64(_))),
"Arguments count should be int64"
);
}
#[tokio::test]
async fn test_empty_document_round_trip() {
let doc = create_rust_document("");
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
assert!(matches!(value, Value::Struct(_)), "Output should be Struct");
assert_eq!(
extract_symbol_count(&value),
0,
"Empty doc should have 0 symbols"
);
assert_eq!(
extract_import_count(&value),
0,
"Empty doc should have 0 imports"
);
assert_eq!(
extract_call_count(&value),
0,
"Empty doc should have 0 calls"
);
let fingerprint_bytes = extract_fingerprint(&value);
assert!(
!fingerprint_bytes.is_empty(),
"Fingerprint should exist for empty doc"
);
}
#[tokio::test]
async fn test_simple_function_round_trip() {
let content = "fn test_function() { println!(\"hello\"); }";
let mut doc = create_rust_document(content);
let metadata = extract_basic_metadata(&doc).expect("Metadata extraction should succeed");
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
let symbol_count = extract_symbol_count(&value);
println!("Symbol count: {}", symbol_count);
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(symbols) = &fields[0]
{
for symbol in symbols {
validate_symbol_structure(symbol);
}
}
}
#[tokio::test]
async fn test_fingerprint_consistency() {
let content = "fn main() { let x = 42; }";
let doc1 = create_rust_document(content);
let doc2 = create_rust_document(content);
let value1 = serialize_parsed_doc(&doc1).expect("Serialization 1 should succeed");
let value2 = serialize_parsed_doc(&doc2).expect("Serialization 2 should succeed");
let fp1 = extract_fingerprint(&value1);
let fp2 = extract_fingerprint(&value2);
assert_eq!(fp1, fp2, "Same content should produce same fingerprint");
}
#[tokio::test]
async fn test_fingerprint_uniqueness() {
let content1 = "fn main() {}";
let content2 = "fn test() {}";
let doc1 = create_rust_document(content1);
let doc2 = create_rust_document(content2);
let value1 = serialize_parsed_doc(&doc1).expect("Serialization 1 should succeed");
let value2 = serialize_parsed_doc(&doc2).expect("Serialization 2 should succeed");
let fp1 = extract_fingerprint(&value1);
let fp2 = extract_fingerprint(&value2);
assert_ne!(
fp1, fp2,
"Different content should produce different fingerprints"
);
}
#[tokio::test]
async fn test_symbol_data_preservation() {
let content = "fn calculate_sum(a: i32, b: i32) -> i32 { a + b }";
let mut doc = create_rust_document(content);
let mut metadata = extract_basic_metadata(&doc).unwrap_or_default();
metadata.defined_symbols.insert(
"calculate_sum".to_string(),
SymbolInfo {
name: "calculate_sum".to_string(),
kind: SymbolKind::Function,
position: thread_ast_engine::Position::new(0, 0, 0),
scope: "global".to_string(),
visibility: Visibility::Public,
},
);
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(symbols) = &fields[0]
{
assert_eq!(symbols.len(), 1, "Should have 1 symbol");
let symbol = &symbols[0];
validate_symbol_structure(symbol);
let ScopeValue(FieldValues {
fields: symbol_fields,
}) = symbol;
if let Value::Basic(BasicValue::Str(name)) = &symbol_fields[0] {
assert_eq!(
name.as_ref(),
"calculate_sum",
"Symbol name should be preserved"
);
}
}
}
#[tokio::test]
async fn test_multiple_symbols_preservation() {
let content = r#"
fn function1() {}
fn function2() {}
fn function3() {}
"#;
let mut doc = create_rust_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(symbols) = &fields[0]
{
println!("Found {} symbols", symbols.len());
for symbol in symbols {
validate_symbol_structure(symbol);
}
}
}
#[tokio::test]
async fn test_import_data_preservation() {
let content = "use std::collections::HashMap;";
let mut doc = create_rust_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(imports) = &fields[1]
{
println!("Found {} imports", imports.len());
for import in imports {
validate_import_structure(import);
}
}
}
#[tokio::test]
async fn test_call_data_preservation() {
let content = "fn main() { println!(\"test\"); }";
let mut doc = create_rust_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(calls) = &fields[2]
{
println!("Found {} calls", calls.len());
for call in calls {
validate_call_structure(call);
}
}
}
#[tokio::test]
async fn test_complex_document_round_trip() {
let content = r#"
use std::collections::HashMap;
fn calculate(x: i32, y: i32) -> i32 {
let result = x + y;
println!("Result: {}", result);
result
}
fn process_data(data: HashMap<String, i32>) {
for (key, value) in data.iter() {
calculate(value, 10);
}
}
"#;
let mut doc = create_rust_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Serialization should succeed");
assert!(matches!(value, Value::Struct(_)), "Output should be Struct");
if let Value::Struct(FieldValues { fields }) = &value {
assert_eq!(fields.len(), 4, "Should have 4 fields");
if let Value::LTable(symbols) = &fields[0] {
for symbol in symbols {
validate_symbol_structure(symbol);
}
}
if let Value::LTable(imports) = &fields[1] {
for import in imports {
validate_import_structure(import);
}
}
if let Value::LTable(calls) = &fields[2] {
for call in calls {
validate_call_structure(call);
}
}
assert!(
matches!(&fields[3], Value::Basic(BasicValue::Bytes(_))),
"Fingerprint should be bytes"
);
}
}
#[tokio::test]
async fn test_unicode_content_round_trip() {
let content = "fn 测试函数() { println!(\"你好世界\"); }";
let doc = create_rust_document(content);
let value = serialize_parsed_doc(&doc).expect("Unicode content should serialize");
let fingerprint = extract_fingerprint(&value);
assert!(
!fingerprint.is_empty(),
"Unicode content should have fingerprint"
);
}
#[tokio::test]
async fn test_large_document_round_trip() {
let mut content = String::new();
for i in 0..100 {
content.push_str(&format!("fn function_{}() {{ println!(\"test\"); }}\n", i));
}
let mut doc = create_rust_document(&content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Large document should serialize");
if let Value::Struct(FieldValues { fields }) = &value
&& let Value::LTable(symbols) = &fields[0]
{
println!("Large document has {} symbols", symbols.len());
for symbol in symbols.iter().take(5) {
validate_symbol_structure(symbol);
}
}
}
#[tokio::test]
async fn test_python_round_trip() {
let content = r#"
def calculate(x, y):
return x + y
def main():
result = calculate(1, 2)
print(result)
"#;
let mut doc = create_python_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("Python serialization should succeed");
assert!(
matches!(value, Value::Struct(_)),
"Python output should be Struct"
);
}
#[tokio::test]
async fn test_typescript_round_trip() {
let content = r#"
function calculate(x: number, y: number): number {
return x + y;
}
const result = calculate(1, 2);
console.log(result);
"#;
let mut doc = create_typescript_document(content);
let metadata = extract_basic_metadata(&doc).unwrap_or_default();
doc.metadata = metadata;
let value = serialize_parsed_doc(&doc).expect("TypeScript serialization should succeed");
assert!(
matches!(value, Value::Struct(_)),
"TypeScript output should be Struct"
);
}
#[tokio::test]
async fn test_malformed_content_handling() {
let content = "fn invalid { this is not valid rust syntax )))";
let doc = create_rust_document(content);
let value = serialize_parsed_doc(&doc).expect("Should serialize even with invalid syntax");
assert!(
matches!(value, Value::Struct(_)),
"Invalid syntax should still produce Struct"
);
let fingerprint = extract_fingerprint(&value);
assert!(
!fingerprint.is_empty(),
"Invalid syntax should still have fingerprint"
);
}