use std::collections::BTreeMap;
use onetaskgraph_plugin_api::{
DependencyEdge, DependencyEndpoint, Designator, EndpointRefusal, ItemKind, NativeId, SourceName,
};
use serde_json::json;
#[test]
fn a_backend_designator_is_the_digest_of_plugin_and_identifier() {
let designator = Designator::of_backend("linear", "0b6c1d6e-org").expect("a plugin kind");
assert_eq!(
designator.as_str(),
format!("linear/{}", sha256_hex(b"linear\n0b6c1d6e-org"))
);
assert_eq!(designator.prefix(), "linear");
assert!(!designator.as_str().contains("0b6c1d6e-org"));
assert_eq!(
Designator::of_backend("linear", "0b6c1d6e-org").expect("a plugin kind"),
designator,
"the same backend designates the same way every time"
);
assert_ne!(
Designator::of_backend("github-projects", "0b6c1d6e-org").expect("a plugin kind"),
designator
);
for refused in ["alias", "Linear", "", "two/parts"] {
assert!(
Designator::of_backend(refused, "org").is_err(),
"{refused:?} cannot prefix a designator"
);
}
}
fn sha256_hex(bytes: &[u8]) -> String {
use sha2::{Digest, Sha256};
Sha256::digest(bytes)
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[test]
fn an_alias_designator_holds_the_pattern() {
let alias = Designator::of_alias("planning_notes-01").expect("a valid alias");
assert_eq!(alias.as_str(), "alias/planning_notes-01");
assert!(alias.is_alias());
for refused in [
"short",
"has a space in it ok",
"dots.are.not.allowed.here",
&"x".repeat(65),
] {
let message = Designator::of_alias(refused).expect_err(refused);
assert!(!message.contains(refused), "{message} repeats the value");
}
assert!(Designator::of_alias(&"x".repeat(64)).is_ok());
assert!(Designator::of_alias(&"x".repeat(16)).is_ok());
}
#[test]
fn the_opaque_form_round_trips_as_exactly_three_keys() {
let designator = Designator::of_backend("linear", "org").expect("a plugin kind");
let endpoint =
DependencyEndpoint::opaque(designator.clone(), "uuid-1".to_owned(), ItemKind::Task)
.expect("an opaque endpoint");
let written = serde_json::to_value(&endpoint).expect("serializes");
assert_eq!(
written,
json!({"designator": designator.as_str(), "id": "uuid-1", "kind": "task"})
);
let read: DependencyEndpoint = serde_json::from_value(written).expect("reads back");
assert_eq!(read, endpoint);
assert_eq!(read.designator(), Some(&designator));
assert!(!read.is_native() && !read.is_qualified());
assert_eq!(read.source(), None);
for malformed in [
json!({"designator": "linear/abc", "id": "x", "kind": "task"}),
json!({"designator": "Linear/".to_owned() + &"a".repeat(64), "id": "x", "kind": "task"}),
json!({"designator": "alias/short", "id": "x", "kind": "task"}),
json!({"designator": designator.as_str(), "id": "", "kind": "task"}),
] {
assert!(
serde_json::from_value::<DependencyEndpoint>(malformed.clone()).is_err(),
"{malformed} is refused"
);
}
}
#[test]
fn a_caller_endpoint_with_another_key_is_refused_naming_the_key() {
let designator = Designator::of_backend("linear", "org").expect("a plugin kind");
for key in [
"title", "team", "project", "labels", "status", "url", "anything",
] {
let given = json!({
"designator": designator.as_str(),
"id": "uuid-1",
"kind": "task",
key: "Secret launch plan",
});
let refused = DependencyEndpoint::from_value(&given).expect_err(key);
assert_eq!(refused, EndpointRefusal::Key(key.to_owned()));
assert!(!refused.to_string().contains("Secret launch plan"));
}
let accepted = DependencyEndpoint::from_value(&json!({
"designator": designator.as_str(), "id": "uuid-1", "kind": "project"
}))
.expect("the three keys alone");
assert_eq!(accepted.kind, ItemKind::Project);
}
#[test]
fn a_malformed_caller_endpoint_is_refused_without_repeating_it() {
let designator = Designator::of_backend("linear", "org").expect("a plugin kind");
for given in [
json!({"designator": designator.as_str(), "id": "uuid-1", "kind": "Secret launch plan"}),
json!({"designator": designator.as_str(), "id": ["Secret launch plan"], "kind": "task"}),
json!({"designator": "Secret launch plan", "id": "uuid-1", "kind": "task"}),
json!({"id": "board:uuid-1", "kind": "Secret launch plan"}),
] {
let refused = DependencyEndpoint::from_value(&given).expect_err("malformed");
assert_eq!(refused, EndpointRefusal::Malformed, "{given}");
assert!(
!refused.to_string().contains("Secret launch plan"),
"{given}: {refused}"
);
}
}
#[test]
fn a_recorded_opaque_far_end_is_never_a_native_relation() {
let designator = Designator::of_backend("linear", "org").expect("a plugin kind");
let metadata = BTreeMap::from([(
DependencyEdge::RECORDED_KEY.to_owned(),
json!([{"designator": designator.as_str(), "id": "uuid-1", "kind": "task"}]),
)]);
let edges = DependencyEdge::recorded(
&metadata,
&NativeId("near".to_owned()),
ItemKind::Task,
&SourceName::new("board").expect("a name"),
Some(ItemKind::Task),
)
.expect("an opaque far end is recorded");
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].to.designator(), Some(&designator));
}