use sqlitegraph::SqliteGraph;
use sqlitegraph::graph::{GraphEdge, GraphEntity};
#[test]
fn spec_snapshot_metadata_persisted() {
let graph = SqliteGraph::open_in_memory().unwrap();
let snapshot_id = "test_snapshot_001";
let timestamp = graph.create_snapshot(snapshot_id).unwrap();
assert!(timestamp > 0, "Snapshot timestamp positive");
let record_exists: bool = graph
.with_connection(|conn| {
let mut stmt = conn
.prepare("SELECT EXISTS(SELECT 1 FROM snapshots WHERE snapshot_id=?1 LIMIT 1)")
.unwrap();
let exists: i64 = stmt.query_row([snapshot_id], |row| row.get(0)).unwrap();
Ok(exists == 1)
})
.unwrap();
assert!(record_exists, "Snapshot metadata should be persisted");
}
#[test]
fn spec_entity_insert_tagged_with_snapshot() {
let graph = SqliteGraph::open_in_memory().unwrap();
let snapshot_id = "entity_snapshot_001";
let _timestamp = graph.create_snapshot(snapshot_id).unwrap();
let entities = vec![GraphEntity {
id: 0,
kind: "Agent".to_string(),
name: "agent1".to_string(),
file_path: None,
data: serde_json::json!({"role": "worker"}),
}];
let ids = graph
.batch_insert_entities_with_snapshot(&entities, snapshot_id)
.unwrap();
assert_eq!(ids.len(), 1);
let tagged: bool = graph
.with_connection(|conn| {
let mut stmt = conn
.prepare("SELECT snapshot_id FROM graph_entities WHERE id=?1")
.unwrap();
let tag: String = stmt.query_row([ids[0]], |row| row.get(0)).unwrap();
Ok(tag == snapshot_id)
})
.unwrap();
assert!(tagged, "Entity should be tagged with snapshot_id");
}
#[test]
fn spec_edge_insert_tagged_with_snapshot() {
let graph = SqliteGraph::open_in_memory().unwrap();
let entity1 = graph
.insert_entity(&GraphEntity {
id: 0,
kind: "Node".to_string(),
name: "node1".to_string(),
file_path: None,
data: serde_json::json!({}),
})
.unwrap();
let entity2 = graph
.insert_entity(&GraphEntity {
id: 0,
kind: "Node".to_string(),
name: "node2".to_string(),
file_path: None,
data: serde_json::json!({}),
})
.unwrap();
let snapshot_id = "edge_snapshot_001";
let _timestamp = graph.create_snapshot(snapshot_id).unwrap();
let edges = vec![GraphEdge {
id: 0,
from_id: entity1,
to_id: entity2,
edge_type: "connects".to_string(),
data: serde_json::json!({"weight": 1.0}),
}];
let ids = graph
.batch_insert_edges_with_snapshot(&edges, snapshot_id)
.unwrap();
assert_eq!(ids.len(), 1);
let tagged: bool = graph
.with_connection(|conn| {
let mut stmt = conn
.prepare("SELECT snapshot_id FROM graph_edges WHERE id=?1")
.unwrap();
let tag: String = stmt.query_row([ids[0]], |row| row.get(0)).unwrap();
Ok(tag == snapshot_id)
})
.unwrap();
assert!(tagged, "Edge should be tagged with snapshot_id");
}
#[test]
fn spec_time_travel_filters_by_visible_at() {
let graph = SqliteGraph::open_in_memory().unwrap();
let snapshot_id_1 = "snapshot_t1";
let timestamp_t1 = graph.create_snapshot(snapshot_id_1).unwrap();
let entities_v1 = vec![GraphEntity {
id: 0,
kind: "Agent".to_string(),
name: "agent1".to_string(),
file_path: None,
data: serde_json::json!({"version": 1}),
}];
let _ids = graph
.batch_insert_entities_with_snapshot(&entities_v1, snapshot_id_1)
.unwrap();
let stats_t1 = graph.query_as_of(timestamp_t1).unwrap();
assert_eq!(stats_t1.total_entities, 1, "t1 should see initial entity");
std::thread::sleep(std::time::Duration::from_secs(2));
let snapshot_id_2 = "snapshot_t2";
let timestamp_t2 = graph.create_snapshot(snapshot_id_2).unwrap();
let entities_v2 = vec![GraphEntity {
id: 0,
kind: "Agent".to_string(),
name: "agent2".to_string(),
file_path: None,
data: serde_json::json!({"version": 2}),
}];
let _ids = graph
.batch_insert_entities_with_snapshot(&entities_v2, snapshot_id_2)
.unwrap();
let stats_t2 = graph.query_as_of(timestamp_t2).unwrap();
assert_eq!(stats_t2.total_entities, 2, "t2 should see both entities");
let stats_back = graph.query_as_of(timestamp_t1).unwrap();
assert_eq!(
stats_back.total_entities, 1,
"time-travel to t1 should see 1 entity"
);
}
#[test]
fn spec_list_snapshots_returns_metadata() {
let graph = SqliteGraph::open_in_memory().unwrap();
let snapshot_id = "list_test_snapshot";
let timestamp = graph.create_snapshot(snapshot_id).unwrap();
let snapshots = graph.list_snapshots().unwrap();
assert!(!snapshots.is_empty(), "Should have at least one snapshot");
let found = snapshots
.iter()
.any(|s| s.snapshot_id == snapshot_id && s.timestamp == timestamp);
assert!(found, "Created snapshot should appear in list");
}
#[test]
fn spec_snapshot_deletion_cascades() {
let graph = SqliteGraph::open_in_memory().unwrap();
let snapshot_id = "delete_test_snapshot";
let _timestamp = graph.create_snapshot(snapshot_id).unwrap();
graph.delete_snapshot(snapshot_id).unwrap();
let exists: bool = graph
.with_connection(|conn| {
let mut stmt = conn
.prepare("SELECT EXISTS(SELECT 1 FROM snapshots WHERE snapshot_id=?1)")
.unwrap();
let ex: i64 = stmt.query_row([snapshot_id], |row| row.get(0)).unwrap();
Ok(ex == 1)
})
.unwrap();
assert!(!exists, "Snapshot metadata should be deleted");
}