use std::path::PathBuf;
use dk_core::{CallEdge, CallKind, Span, Symbol, SymbolKind, Visibility};
use dk_engine::workspace::session_graph::SessionGraph;
use uuid::Uuid;
fn make_symbol(name: &str) -> Symbol {
Symbol {
id: Uuid::new_v4(),
name: name.to_string(),
qualified_name: name.to_string(),
kind: SymbolKind::Function,
visibility: Visibility::Public,
file_path: PathBuf::from("test.rs"),
span: Span {
start_byte: 0,
end_byte: 10,
},
signature: None,
doc_comment: None,
parent: None,
last_modified_by: None,
last_modified_intent: None,
}
}
fn make_edge(caller: Uuid, callee: Uuid) -> CallEdge {
CallEdge {
id: Uuid::new_v4(),
repo_id: Uuid::new_v4(),
caller,
callee,
kind: CallKind::DirectCall,
}
}
#[test]
fn roundtrip_empty_graph() {
let g = SessionGraph::empty();
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
assert_eq!(g2.change_count(), 0);
}
#[test]
fn roundtrip_added_symbols() {
let g = SessionGraph::empty();
let sym_a = make_symbol("alpha");
let sym_b = make_symbol("beta");
let id_a = sym_a.id;
let id_b = sym_b.id;
g.add_symbol(sym_a);
g.add_symbol(sym_b);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
let got_a = g2.get_symbol(id_a).expect("alpha not found after roundtrip");
assert_eq!(got_a.name, "alpha");
let got_b = g2.get_symbol(id_b).expect("beta not found after roundtrip");
assert_eq!(got_b.name, "beta");
assert_eq!(g2.change_count(), 2);
}
#[test]
fn roundtrip_modified_symbols() {
let g = SessionGraph::empty();
let mut sym = make_symbol("my_fn");
let id = sym.id;
g.modify_symbol(sym.clone());
sym.name = "my_fn_v2".to_string();
g.modify_symbol(sym);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
let got = g2.get_symbol(id).expect("symbol not found after roundtrip");
assert_eq!(got.name, "my_fn_v2");
}
#[test]
fn roundtrip_removed_symbols() {
let g = SessionGraph::empty();
let sym1 = make_symbol("gone_fn_1");
let sym2 = make_symbol("gone_fn_2");
let id1 = sym1.id;
let id2 = sym2.id;
g.remove_symbol(id1);
g.remove_symbol(id2);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
assert!(g2.get_symbol(id1).is_none());
assert!(g2.get_symbol(id2).is_none());
assert_eq!(g2.change_count(), 2);
}
#[test]
fn roundtrip_added_edges() {
let g = SessionGraph::empty();
let sym_a = make_symbol("caller_fn");
let sym_b = make_symbol("callee_fn");
let edge = make_edge(sym_a.id, sym_b.id);
let edge_id = edge.id;
let expected_caller = sym_a.id;
let expected_callee = sym_b.id;
g.add_edge(edge);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
let got = g2.get_edge(edge_id).expect("edge not found after roundtrip");
assert_eq!(got.caller, expected_caller);
assert_eq!(got.callee, expected_callee);
assert_eq!(got.kind, CallKind::DirectCall);
}
#[test]
fn roundtrip_removed_edges() {
let g = SessionGraph::empty();
let edge_id_1 = Uuid::new_v4();
let edge_id_2 = Uuid::new_v4();
g.remove_edge(edge_id_1);
g.remove_edge(edge_id_2);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
assert!(g2.is_edge_removed(edge_id_1));
assert!(g2.is_edge_removed(edge_id_2));
}
#[test]
fn roundtrip_mixed_delta() {
let g = SessionGraph::empty();
let added = make_symbol("new_fn");
let added_id = added.id;
g.add_symbol(added);
let modified = make_symbol("changed_fn");
let modified_id = modified.id;
g.modify_symbol(modified);
let removed_id = Uuid::new_v4();
g.remove_symbol(removed_id);
let edge = make_edge(added_id, modified_id);
let edge_id = edge.id;
g.add_edge(edge);
let removed_edge_id = Uuid::new_v4();
g.remove_edge(removed_edge_id);
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
assert!(g2.get_symbol(added_id).is_some());
assert!(g2.get_symbol(modified_id).is_some());
assert!(g2.get_symbol(removed_id).is_none());
assert!(g2.get_edge(edge_id).is_some());
assert!(g2.is_edge_removed(removed_edge_id));
assert_eq!(g2.change_count(), 3);
}
#[test]
fn base_symbols_not_included_in_snapshot() {
use arc_swap::ArcSwap;
use std::collections::HashMap;
use std::sync::Arc;
let mut base = HashMap::new();
let base_sym = make_symbol("base_only_fn");
let base_id = base_sym.id;
base.insert(base_id, base_sym);
let shared = Arc::new(ArcSwap::from_pointee(base));
let g = SessionGraph::fork_from(shared);
assert!(g.get_symbol(base_id).is_some());
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
assert!(g2.get_symbol(base_id).is_none());
}
#[test]
fn msgpack_bytes_are_nonempty() {
let g = SessionGraph::empty();
let sym = make_symbol("foo");
g.add_symbol(sym);
let bytes = g.to_msgpack().expect("to_msgpack failed");
assert!(!bytes.is_empty());
}
#[test]
fn invalid_bytes_return_error() {
let garbage = b"this is not msgpack";
let result = SessionGraph::from_msgpack(garbage);
assert!(result.is_err());
}
#[test]
fn symbol_fields_preserved() {
let g = SessionGraph::empty();
let sym = Symbol {
id: Uuid::new_v4(),
name: "complex_fn".to_string(),
qualified_name: "my_module::complex_fn".to_string(),
kind: SymbolKind::Function,
visibility: Visibility::Crate,
file_path: PathBuf::from("src/complex.rs"),
span: Span {
start_byte: 42,
end_byte: 99,
},
signature: Some("fn complex_fn(x: u32) -> bool".to_string()),
doc_comment: Some("/// Does something complex".to_string()),
parent: Some(Uuid::new_v4()),
last_modified_by: Some("agent-007".to_string()),
last_modified_intent: Some("refactor".to_string()),
};
let id = sym.id;
g.add_symbol(sym.clone());
let bytes = g.to_msgpack().expect("to_msgpack failed");
let g2 = SessionGraph::from_msgpack(&bytes).expect("from_msgpack failed");
let got = g2.get_symbol(id).expect("symbol not found");
assert_eq!(got.name, "complex_fn");
assert_eq!(got.qualified_name, "my_module::complex_fn");
assert_eq!(got.kind, SymbolKind::Function);
assert_eq!(got.visibility, Visibility::Crate);
assert_eq!(got.file_path, PathBuf::from("src/complex.rs"));
assert_eq!(got.span.start_byte, 42);
assert_eq!(got.span.end_byte, 99);
assert_eq!(got.signature.as_deref(), Some("fn complex_fn(x: u32) -> bool"));
assert_eq!(got.doc_comment.as_deref(), Some("/// Does something complex"));
assert_eq!(got.last_modified_by.as_deref(), Some("agent-007"));
assert_eq!(got.last_modified_intent.as_deref(), Some("refactor"));
assert_eq!(got.parent, sym.parent);
}