use selene_core::PropertyValueType;
use super::sections::{
decode_edges, decode_graph_types, decode_meta, decode_nodes, encode_edges, encode_graph_types,
encode_meta,
};
use super::*;
use crate::graph_types::{GraphTypeDef, NodeTypeDef, PropertyTypeDef, ValidationMode};
#[test]
fn decode_nodes_rejects_duplicate_committed_id() {
let mut graph = SeleneGraph::new(GraphId::new(2_000));
for _ in 0..2 {
graph.node_store.labels.push(LabelSet::new());
graph.node_store.properties.push(PropertyMap::new());
graph.node_store.row_to_id.push(NodeId::new(5)); }
graph.node_store.alive_mut().insert(0);
graph.node_store.alive_mut().insert(1);
let bytes = encode_nodes(&graph).unwrap();
let err = decode_nodes(&bytes)
.expect_err("decode_nodes must reject a duplicate committed id via validate_ids_unique");
assert!(
matches!(&err, ProviderError::InvalidPayload { reason } if reason.contains("unique non-tombstone")),
"expected validate_ids_unique rejection, got {err:?}",
);
}
#[test]
fn decode_edges_rejects_duplicate_committed_id() {
let mut graph = SeleneGraph::new(GraphId::new(2_001));
let label = db_string("dup.edge").unwrap();
for _ in 0..2 {
graph.edge_store.label.push(label.clone());
graph.edge_store.source.push(NodeId::new(1));
graph.edge_store.target.push(NodeId::new(2));
graph.edge_store.properties.push(PropertyMap::new());
graph.edge_store.row_to_id.push(EdgeId::new(5)); }
graph.edge_store.alive_mut().insert(0);
graph.edge_store.alive_mut().insert(1);
let bytes = encode_edges(&graph).unwrap();
let err = decode_edges(&bytes)
.expect_err("decode_edges must reject a duplicate committed id via validate_ids_unique");
assert!(
matches!(&err, ProviderError::InvalidPayload { reason } if reason.contains("unique non-tombstone")),
"expected validate_ids_unique rejection, got {err:?}",
);
}
#[test]
fn bytecheck_rejects_truncated_edge_section() {
let graph = graph_with_edge();
let mut bytes = encode_edges(&graph).unwrap();
let new_len = bytes.len().saturating_sub(16);
bytes.truncate(new_len);
assert!(
matches!(
decode_edges(&bytes),
Err(ProviderError::InvalidPayload { reason }) if reason.contains("bytecheck")
),
"truncated CORE/EDGE must fail bytecheck",
);
}
#[test]
fn bytecheck_rejects_corrupted_edge_root_pointer() {
let graph = graph_with_edge();
let mut bytes = encode_edges(&graph).unwrap();
let last = bytes.len() - 1;
bytes[last] ^= 0xFF;
assert!(
matches!(
decode_edges(&bytes),
Err(ProviderError::InvalidPayload { reason }) if reason.contains("bytecheck")
),
"corrupted CORE/EDGE root pointer must fail bytecheck",
);
}
#[test]
fn bytecheck_rejects_truncated_meta_section() {
let graph = graph_with_node();
let mut bytes = encode_meta(&graph.meta, 7).unwrap();
let new_len = bytes.len().saturating_sub(8);
bytes.truncate(new_len);
assert!(
matches!(
decode_meta(&bytes),
Err(ProviderError::InvalidPayload { reason }) if reason.contains("bytecheck")
),
"truncated CORE/META must fail bytecheck",
);
}
#[test]
fn bytecheck_rejects_truncated_meta_to_one_byte() {
let graph = graph_with_node();
let bytes = encode_meta(&graph.meta, 7).unwrap();
let truncated = &bytes[..1];
assert!(
matches!(
decode_meta(truncated),
Err(ProviderError::InvalidPayload { reason }) if reason.contains("bytecheck")
),
"a 1-byte CORE/META must fail bytecheck",
);
}
#[test]
fn bytecheck_rejects_corrupted_gtyp_rkyv_body() {
let person = db_string("CorruptGtypPerson").unwrap();
let graph_type = GraphTypeDef {
name: db_string("corrupt.gtyp.graph").unwrap(),
node_types: vec![NodeTypeDef {
name: person.clone(),
key_labels: LabelSet::single(person),
properties: vec![PropertyTypeDef {
name: db_string("serial").unwrap(),
value_type: PropertyValueType::String,
list_element_type: None,
required: false,
default: None,
immutable: false,
unique: false,
decimal_type: None,
character_string_type: None,
byte_string_type: None,
record_field_types: None,
}],
validation_mode: ValidationMode::Strict,
}],
edge_types: Vec::new(),
};
let graph = SharedGraph::builder(GraphId::new(2_010))
.bound_to(graph_type)
.unwrap()
.build()
.unwrap()
.read()
.as_ref()
.clone();
let mut bytes = encode_graph_types(&graph).unwrap();
assert_eq!(decode_graph_types(&bytes).unwrap().len(), 1);
let last = bytes.len() - 1;
bytes[last] ^= 0xFF;
assert!(
matches!(
decode_graph_types(&bytes),
Err(ProviderError::InvalidPayload { reason }) if reason.contains("bytecheck")
),
"corrupted CORE/GTYP rkyv body must fail bytecheck",
);
}