mod common;
use common::*;
use zenith_tx::{Op, Permissions, Transaction, TxStatus, run_transaction};
const TOKEN_DOC: &str = r##"zenith version=1 {
project id="proj" name="Test"
tokens format="zenith-token-v1" {
token id="color.accent" type="color" value="#3b82f6"
token id="size.base" type="dimension" value=(px)16
}
styles { }
document id="doc1" title="T" {
page id="pg1" w=(px)400 h=(px)300 {
rect id="r1" x=(px)0 y=(px)0 w=(px)100 h=(px)100 fill=(token)"color.accent"
}
}
}"##;
#[test]
fn create_token_color_accepted() {
let doc = parse(TOKEN_DOC);
let initial_count = doc.tokens.tokens.len();
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "color.brand".to_owned(),
token_type: "color".to_owned(),
value: "#e11d48".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Accepted,
"expected Accepted; diagnostics: {:?}",
result.diagnostics
);
assert_eq!(
result.affected_node_ids,
vec!["color.brand".to_owned()],
"affected must contain the new token id"
);
assert!(
result.source_after.contains("id=\"color.brand\""),
"source_after must contain the new token id; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("#e11d48"),
"source_after must contain the token value; got:\n{}",
result.source_after
);
let after_doc = parse(&result.source_after);
assert_eq!(
after_doc.tokens.tokens.len(),
initial_count + 1,
"token count should increase by 1"
);
}
#[test]
fn create_token_dimension_accepted() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "size.new".to_owned(),
token_type: "dimension".to_owned(),
value: "(px)40".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Accepted,
"expected Accepted; diagnostics: {:?}",
result.diagnostics
);
assert!(
result.source_after.contains("id=\"size.new\""),
"source_after must contain the new token id; got:\n{}",
result.source_after
);
assert!(
result.source_after.contains("(px)"),
"source_after must contain the (px) dimension unit; got:\n{}",
result.source_after
);
}
#[test]
fn create_token_duplicate_id_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "color.accent".to_owned(),
token_type: "color".to_owned(),
value: "#ffffff".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.duplicate_id"),
"expected tx.duplicate_id; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}
#[test]
fn create_token_gradient_type_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "grad.new".to_owned(),
token_type: "gradient".to_owned(),
value: "#ff0000".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.invalid_value"),
"expected tx.invalid_value; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}
#[test]
fn create_token_unparseable_dimension_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "size.bad".to_owned(),
token_type: "dimension".to_owned(),
value: "not-a-dimension".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.invalid_value"),
"expected tx.invalid_value; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}
#[test]
fn create_token_unparseable_number_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::CreateToken {
id: "num.bad".to_owned(),
token_type: "number".to_owned(),
value: "NaN".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.invalid_value"),
"expected tx.invalid_value; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}
#[test]
fn update_token_value_dimension_accepted() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::UpdateTokenValue {
id: "size.base".to_owned(),
value: "(px)40".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Accepted,
"expected Accepted; diagnostics: {:?}",
result.diagnostics
);
assert_eq!(
result.affected_node_ids,
vec!["size.base".to_owned()],
"affected must contain the updated token id"
);
assert!(
result.source_after.contains("id=\"size.base\""),
"source_after must still contain size.base; got:\n{}",
result.source_after
);
let after_doc = parse(&result.source_after);
let updated = after_doc
.tokens
.tokens
.iter()
.find(|t| t.id == "size.base")
.expect("size.base must still exist");
assert!(
matches!(updated.token_type, zenith_core::TokenType::Dimension),
"token type must remain Dimension; got: {:?}",
updated.token_type
);
}
#[test]
fn update_token_value_unknown_id_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::UpdateTokenValue {
id: "color.does_not_exist".to_owned(),
value: "#ffffff".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.unknown_token"),
"expected tx.unknown_token; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}
#[test]
fn update_token_value_type_mismatch_rejected() {
let doc = parse(TOKEN_DOC);
let tx = Transaction {
ops: vec![Op::UpdateTokenValue {
id: "size.base".to_owned(),
value: "Inter".to_owned(),
}],
permissions: Permissions::default(),
};
let result = run_transaction(&doc, &tx).expect("run_transaction should not error");
assert_eq!(
result.status,
TxStatus::Rejected,
"expected Rejected; diagnostics: {:?}",
result.diagnostics
);
assert!(
result
.diagnostics
.iter()
.any(|d| d.code == "tx.invalid_value"),
"expected tx.invalid_value; got: {:?}",
result.diagnostics
);
assert_eq!(result.source_after, result.source_before);
}