generated from eblume/project-template
Some checks failed
Build / validate (pull_request) Failing after 11s
`wikilink::to_ids` rewrites name-addressed links to the canonical id (id-first resolve: an already-id target is left alone, a name → its id with any label preserved). `Store::migrate_wikilinks_to_ids` runs it over every body and re-saves through update_node (which collapses + materializes by id); idempotent. Surfaced as the `migrate.wikilinks` RPC + RemoteStore forward + the `heph migrate-links` CLI command (not auto-run — the owner runs it once per store). Name-resolution + the canonical-context hack stay for now so legacy links keep resolving pre-migration; retiring them is a later tidy. Tests: to_ids unit + a heph-core migrate integration (rewrite + materialize + idempotency). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
2.5 KiB
Rust
64 lines
2.5 KiB
Rust
//! Wiki-link display projection (tech-spec §8.4): a body's links are stored as
|
|
//! canonical bare `[[NODEID]]`. `update_node` collapses a name-matching
|
|
//! `[[NODEID|Name]]` label back to bare (the daemon expands it again on read);
|
|
//! a custom label is preserved. `get_node` here returns the **raw stored** body
|
|
//! (expansion is a daemon-read concern), so it shows the collapsed form.
|
|
|
|
use heph_core::{FixedClock, LinkType, LocalStore, NewNode, Store};
|
|
|
|
fn store() -> LocalStore {
|
|
LocalStore::open_in_memory(Box::new(FixedClock(1_700_000_000_000))).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn update_collapses_name_matching_labels_and_materializes_by_id() {
|
|
let mut s = store();
|
|
let target = s.create_node(NewNode::doc("Roof", "")).unwrap();
|
|
let src = s.create_node(NewNode::doc("Daily", "")).unwrap();
|
|
|
|
// The buffer the user saves carries the expanded label `[[id|Roof]]`.
|
|
let updated = s
|
|
.update_node(&src.id, None, Some(format!("see [[{}|Roof]] here", target.id)))
|
|
.unwrap();
|
|
// Stored body collapsed the auto-label back to the canonical bare id.
|
|
assert_eq!(
|
|
updated.body.as_deref(),
|
|
Some(format!("see [[{}]] here", target.id).as_str())
|
|
);
|
|
// And the `[[id]]` materialized as a wiki link by id.
|
|
assert!(s
|
|
.outgoing_links(&src.id)
|
|
.unwrap()
|
|
.iter()
|
|
.any(|l| l.link_type == LinkType::Wiki && l.dst_id == target.id));
|
|
|
|
// A custom label (≠ the target's current name) is a real override — kept.
|
|
let custom = format!("[[{}|my roof]]", target.id);
|
|
let u2 = s.update_node(&src.id, None, Some(custom.clone())).unwrap();
|
|
assert_eq!(u2.body.as_deref(), Some(custom.as_str()));
|
|
}
|
|
|
|
#[test]
|
|
fn migrate_rewrites_legacy_name_links_to_ids() {
|
|
let mut s = store();
|
|
let target = s.create_node(NewNode::doc("Roof", "")).unwrap();
|
|
// A legacy body authored with a name-addressed link (pre-§8.4).
|
|
let src = s
|
|
.create_node(NewNode::doc("Daily", "fix the [[Roof]] soon"))
|
|
.unwrap();
|
|
|
|
let changed = s.migrate_wikilinks_to_ids().unwrap();
|
|
assert_eq!(changed, 1);
|
|
|
|
// The name was rewritten to the canonical bare id, and the wiki link is by id.
|
|
let body = s.get_node(&src.id).unwrap().unwrap().body.unwrap();
|
|
assert_eq!(body, format!("fix the [[{}]] soon", target.id));
|
|
assert!(s
|
|
.outgoing_links(&src.id)
|
|
.unwrap()
|
|
.iter()
|
|
.any(|l| l.link_type == LinkType::Wiki && l.dst_id == target.id));
|
|
|
|
// Idempotent: a second run finds nothing to change.
|
|
assert_eq!(s.migrate_wikilinks_to_ids().unwrap(), 0);
|
|
}
|