refactor(tests): group test files by feature, drop phase/cluster naming
CI / cargo fmt --check (push) Successful in 21s
CI / cargo clippy (push) Successful in 1m14s
CI / cargo test (push) Successful in 1m21s
CI / editor keymaps (push) Failing after 1m31s

Restructures the integration test layout so each file is named after
what it covers, not the implementation phase it landed in.

nuwiki-core (renames only):
  vimwiki_lexer            → lexer
  vimwiki_parser           → parser
  vimwiki_tags             → tags
  vimwiki_table_alignment  → table_alignment
  diary_period             → diary
  list_continuation        → lists
  transclusion_attrs       → transclusion
  + table colspan/rowspan tests moved here from parity_cluster_1

nuwiki-lsp (renames + merges + one split):
  cluster_a_list_rewriters       → commands_lists
  cluster_b_table_rewriters      → commands_tables
  cluster_c_link_helpers +
    phase19_followlink_creates   → commands_links
  phase13_rename_commands        → commands_files
  phase17_colorize               → commands_colorize
  phase17_html_export            → html_export
  phase15_link_health            → link_health
  phase18_multi_wiki             → multi_wiki
  phase19_folding                → folding
  nav                            → navigation
  lsp_helpers                    → helpers
  phase16_diary +
    diary_frequency              → diary
  phase11_plumbing +
    parity_cluster_1 (config)    → index_and_config
  tags_index_and_lsp +
    phase17_backfill             → commands_tags
  phase14_edit_commands          → split into
    commands_checkboxes,
    commands_headings,
    commands_tasks

Net: 30 → 28 integration-test files. 456 → 455 tests (the one
removed test was a dummy `paragraph_render_is_unchanged` whose only
purpose was to keep a `ParagraphNode` import alive in
parity_cluster_1; the import is exercised elsewhere now).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 01:30:55 +00:00
parent 5b6f789c8d
commit f4e086f981
30 changed files with 763 additions and 778 deletions
+122
View File
@@ -0,0 +1,122 @@
//! Link-related LSP commands.
//!
//! Covers:
//! - `nuwiki.link.pasteWikilink` / `nuwiki.link.pasteUrl` — page-name
//! synthesis from a URI and the textual shape the handler inserts
//! at the cursor.
//! - Wikilink resolution, including the regression where `<CR>` on a
//! wikilink to a not-yet-created page must still resolve to a URI
//! so the editor can open (and on save create) the future page.
//! The resolver itself is private; we exercise the synthesised-path
//! semantics via the public config helpers + index lookup.
use std::path::PathBuf;
use nuwiki_core::ast::LinkKind;
use nuwiki_core::syntax::vimwiki::VimwikiSyntax;
use nuwiki_core::syntax::SyntaxPlugin;
use nuwiki_lsp::config::WikiConfig;
use nuwiki_lsp::index::{page_name_from_uri, WorkspaceIndex};
use tower_lsp::lsp_types::Url;
fn parse(src: &str) -> nuwiki_core::ast::DocumentNode {
VimwikiSyntax::new().parse(src)
}
// ===== paste helpers =====
#[test]
fn commands_list_advertises_link_helpers() {
let names: Vec<&str> = nuwiki_lsp::commands::COMMANDS.to_vec();
for name in ["nuwiki.link.pasteWikilink", "nuwiki.link.pasteUrl"] {
assert!(names.contains(&name), "missing: {name}");
}
}
#[test]
fn page_name_for_root_level_page() {
let cfg = WikiConfig::from_root(PathBuf::from("/tmp/wiki"));
let uri = Url::from_file_path("/tmp/wiki/Home.wiki").unwrap();
assert_eq!(page_name_from_uri(&uri, Some(&cfg.root)), "Home");
}
#[test]
fn page_name_for_subdir_page() {
let cfg = WikiConfig::from_root(PathBuf::from("/tmp/wiki"));
let uri = Url::from_file_path("/tmp/wiki/diary/2026-05-11.wiki").unwrap();
assert_eq!(
page_name_from_uri(&uri, Some(&cfg.root)),
"diary/2026-05-11"
);
}
#[test]
fn paste_url_format_matches_export_output_path_for() {
// The URL the `pasteUrl` handler emits is `<name>.html`, where
// `<name>` walks path segments. Mirror that derivation and assert
// the shape we'd insert at cursor.
let name = "diary/2026-05-11";
let mut url = String::new();
for (i, seg) in name.split('/').enumerate() {
if i > 0 {
url.push('/');
}
url.push_str(seg);
}
url.push_str(".html");
assert_eq!(url, "diary/2026-05-11.html");
}
#[test]
fn paste_wikilink_shape_for_root_page() {
// `[[<name>]]` is what the handler inserts at the cursor.
let name = "Notes";
assert_eq!(format!("[[{name}]]"), "[[Notes]]");
}
// ===== resolve follow-link to non-existent page =====
#[test]
fn link_to_indexed_page_resolves_via_index() {
let root = "/tmp/follow-link-1";
let mut idx = WorkspaceIndex::new(Some(PathBuf::from(root)));
let target = Url::from_file_path(format!("{root}/Notes.wiki")).unwrap();
idx.upsert(target.clone(), &parse("= notes =\n"));
let resolved = idx.pages_by_name.get("Notes").cloned();
assert_eq!(resolved, Some(target));
}
#[test]
fn missing_page_is_not_in_index() {
let root = "/tmp/follow-link-2";
let idx = WorkspaceIndex::new(Some(PathBuf::from(root)));
// Empty index — "Missing" isn't there, so the fallback synthesise
// path is the only way to follow `[[Missing]]`.
assert!(!idx.pages_by_name.contains_key("Missing"));
}
#[test]
fn config_root_plus_path_plus_ext_is_a_valid_uri() {
// Sanity: the synthesise logic constructs <root>/<path>.<ext>.
let cfg = WikiConfig::from_root(PathBuf::from("/tmp/follow-link-3"));
assert_eq!(cfg.file_extension, ".wiki");
let target_path = cfg.root.join("Missing.wiki");
assert!(Url::from_file_path(&target_path).is_ok());
}
#[test]
fn wikilink_to_subdir_page_parses_with_slash_in_path() {
// Synthesis must walk path segments, not just concatenate.
let doc = parse("[[notes/Daily]]\n");
let inline = match &doc.children[0] {
nuwiki_core::ast::BlockNode::Paragraph(p) => &p.children[0],
_ => panic!("expected paragraph"),
};
let target = match inline {
nuwiki_core::ast::InlineNode::WikiLink(w) => &w.target,
_ => panic!("expected wikilink"),
};
assert_eq!(target.kind, LinkKind::Wiki);
assert_eq!(target.path.as_deref(), Some("notes/Daily"));
}