mod common;
use common::*;
use zenith_tx::{Op, Permissions, Transaction, TxStatus, run_transaction};
fn accepted(status: TxStatus) -> bool {
matches!(status, TxStatus::Accepted | TxStatus::AcceptedWithWarnings)
}
#[test]
fn set_geometry_on_node_inside_unknown() {
let doc = parse(UNKNOWN_WITH_INNER_DOC);
let tx = Transaction {
ops: vec![Op::SetGeometry {
node: "inner".to_owned(),
x: Some(25.0),
y: None,
w: Some(120.0),
h: None,
rotate: None,
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert!(
accepted(result.status),
"expected non-rejected; diagnostics: {:?}",
result.diagnostics
);
assert_eq!(result.affected_node_ids, vec!["inner".to_owned()]);
assert!(
result.source_after.contains("x=(px)25"),
"inner rect must have new x; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("w=(px)120"),
"inner rect must have new w; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("id=\"lib1\""),
"unknown parent must remain in source_after"
);
}
#[test]
fn remove_node_inside_unknown() {
let doc = parse(UNKNOWN_WITH_INNER_DOC);
let tx = Transaction {
ops: vec![Op::RemoveNode {
node: "inner".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert!(
accepted(result.status),
"expected non-rejected; diagnostics: {:?}",
result.diagnostics
);
assert_eq!(result.affected_node_ids, vec!["inner".to_owned()]);
assert!(
!result.source_after.contains("id=\"inner\""),
"inner must be gone; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("id=\"lib1\""),
"unknown parent must remain after removing its child"
);
}
#[test]
fn remove_unknown_node_by_id() {
let doc = parse(UNKNOWN_WITH_INNER_DOC);
let tx = Transaction {
ops: vec![Op::RemoveNode {
node: "lib1".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert!(
accepted(result.status),
"expected non-rejected; diagnostics: {:?}",
result.diagnostics
);
assert_eq!(result.affected_node_ids, vec!["lib1".to_owned()]);
assert!(
!result.source_after.contains("id=\"lib1\""),
"unknown node must be gone; got:\n{}",
result.source_after
);
assert!(
!result.source_after.contains("id=\"inner\""),
"the unknown node's child must be gone with it; got:\n{}",
result.source_after
);
}
#[test]
fn reparent_into_unknown_rejected() {
let doc = parse(UNKNOWN_PARENT_DOC);
let tx = Transaction {
ops: vec![Op::Reparent {
node: "outer".to_owned(),
new_parent: "lib1".to_owned(), position: zenith_tx::Position::Last,
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(result.status, TxStatus::Rejected);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.invalid_parent"),
"expected tx.invalid_parent; got: {:?}",
result.diagnostics
);
assert_eq!(
result.source_after, result.source_before,
"a rejected transaction must not mutate the document"
);
}
#[test]
fn group_across_unknown_boundary_rejected() {
let doc = parse(UNKNOWN_PARENT_DOC);
let tx = Transaction {
ops: vec![Op::Group {
node_ids: vec!["inner".to_owned(), "outer".to_owned()],
group_id: "grp-bad".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(result.status, TxStatus::Rejected);
assert_eq!(
result.source_after, result.source_before,
"a rejected group must not mutate the document"
);
}
#[test]
fn reorder_inside_unknown_not_errored() {
let doc = parse(UNKNOWN_WITH_INNER_DOC);
let tx = Transaction {
ops: vec![Op::MoveForward {
node: "inner".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert!(
accepted(result.status),
"reorder inside unknown must not be rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result.source_after.contains("id=\"inner\""),
"inner must still be present after a no-op reorder"
);
}
#[test]
fn duplicate_page_suffixes_unknown_subtree_ids() {
let doc = parse(UNKNOWN_WITH_INNER_DOC);
let tx = Transaction {
ops: vec![Op::DuplicatePage {
page: "pg1".to_owned(),
new_id: "pg2".to_owned(),
id_suffix: ".v2".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert!(
accepted(result.status),
"duplicate_page over an unknown subtree must succeed; diagnostics: {:?}",
result.diagnostics
);
assert!(
!result.diagnostics.iter().any(|d| d.code == "id.duplicate"),
"cloned unknown subtree ids must be unique; got: {:?}",
result.diagnostics
);
assert!(
result.source_after.contains("id=\"lib1.v2\""),
"cloned unknown node must carry the suffixed id; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("id=\"inner.v2\""),
"cloned child of unknown node must carry the suffixed id; got:\n{}",
result.source_after
);
assert_eq!(
result.source_after.matches("id=\"lib1\"").count(),
1,
"source unknown node id must remain unique; got:\n{}",
result.source_after
);
assert_eq!(
result.source_after.matches("id=\"inner\"").count(),
1,
"source child id must remain unique; got:\n{}",
result.source_after
);
}