use ronin_core::{completion_context, completions, parse, CompletionKind, PositionKind};
fn ctx(src: &str, offset: usize) -> ronin_core::CompletionContext {
completion_context(&parse(src), offset)
}
#[test]
fn classifies_struct_field_position() {
let src = "Point(x: 1, )";
let c = ctx(src, src.len() - 1);
assert_eq!(c.position, Some(PositionKind::StructField));
}
#[test]
fn classifies_struct_value_position_after_colon() {
let src = "Point(x: )";
let after_colon = src.find(':').unwrap() + 2;
assert_eq!(ctx(src, after_colon).position, Some(PositionKind::Value));
}
#[test]
fn classifies_list_element_position() {
let src = "[1, ]";
assert_eq!(ctx(src, 4).position, Some(PositionKind::ListElement));
}
#[test]
fn classifies_map_key_position() {
let src = "{ alpha: 1 }";
assert_eq!(ctx(src, 2).position, Some(PositionKind::MapKey));
}
#[test]
fn classifies_map_value_position() {
let src = "{ alpha: 1 }";
let after_colon = src.find(':').unwrap() + 2;
assert_eq!(ctx(src, after_colon).position, Some(PositionKind::MapValue));
}
#[test]
fn classifies_tuple_position() {
let src = "(1, 2)";
assert_eq!(ctx(src, 4).position, Some(PositionKind::Tuple));
}
#[test]
fn classifies_top_level_value_position() {
let src = "Foo";
assert_eq!(ctx(src, 3).position, Some(PositionKind::Value));
}
#[test]
fn collects_sibling_field_names_in_enclosing_struct() {
let src = "Point(x: 1, y: 2, )";
let c = ctx(src, src.len() - 1);
assert!(c.sibling_field_names.contains(&"x".to_string()));
assert!(c.sibling_field_names.contains(&"y".to_string()));
}
#[test]
fn collects_in_file_variant_names_anywhere() {
let src = "[Alpha, Beta, ]";
let c = ctx(src, src.len() - 1);
assert!(c.in_file_variant_names.contains(&"Alpha".to_string()));
assert!(c.in_file_variant_names.contains(&"Beta".to_string()));
}
#[test]
fn collects_in_file_map_keys_anywhere() {
let src = "{ alpha: 1, beta: 2 }";
let c = ctx(src, 2);
assert!(c.in_file_map_keys.contains(&"alpha".to_string()));
assert!(c.in_file_map_keys.contains(&"beta".to_string()));
}
#[test]
fn attested_field_name_is_offered_at_a_struct_slot() {
let src = "Rect(width: 1, )";
let c = ctx(src, src.len() - 1);
assert!(
c.items
.iter()
.any(|i| i.label == "width" && i.kind == CompletionKind::Field),
"an attested sibling field name should be offered at a field slot"
);
}
#[test]
fn items_ordered_kind_then_alpha_all_below_literal() {
let src = "[]";
let c = ctx(src, 1);
assert_eq!(c.position, Some(PositionKind::ListElement));
for (i, item) in c.items.iter().enumerate() {
assert!(
item.rank >= 1,
"no suggestion may occupy the literal's rank 0"
);
assert_eq!(item.rank, (i as u32) + 1, "ranks follow the sorted order");
}
let first_option = c
.items
.iter()
.position(|i| i.kind == CompletionKind::Option);
let first_delim = c
.items
.iter()
.position(|i| i.kind == CompletionKind::Delimiter);
assert!(
first_option < first_delim,
"Option suggestions must sort before delimiter suggestions"
);
let none_pos = c.items.iter().position(|i| i.label == "None");
let some_pos = c.items.iter().position(|i| i.label == "Some");
assert!(none_pos < some_pos, "Option labels sort alphabetically");
}
#[test]
fn ordering_is_deterministic_across_runs() {
let src = "[Gamma, Alpha, Beta, ]";
let a = ctx(src, src.len() - 1);
let b = ctx(src, src.len() - 1);
let labels_a: Vec<&str> = a.items.iter().map(|i| i.label.as_str()).collect();
let labels_b: Vec<&str> = b.items.iter().map(|i| i.label.as_str()).collect();
assert_eq!(labels_a, labels_b, "ordering must be deterministic");
let variant_labels: Vec<&str> = a
.items
.iter()
.filter(|i| i.kind == CompletionKind::Variant)
.map(|i| i.label.as_str())
.collect();
let mut sorted = variant_labels.clone();
sorted.sort_unstable();
assert_eq!(variant_labels, sorted, "variants sort alphabetically");
}
#[test]
fn prefix_filters_and_excludes_exact_literal() {
let partial = ctx("So", 2);
assert_eq!(partial.prefix, "So");
assert!(partial.items.iter().any(|i| i.label == "Some"));
assert!(partial.items.iter().all(|i| i.label != "None"));
let exact = ctx("Some", 4);
assert!(exact.items.iter().all(|i| i.label != "Some"));
}
#[test]
fn empty_buffer_is_zero_items_no_error() {
let c = ctx("", 0);
assert_eq!(c.position, None);
assert!(c.items.is_empty());
}
#[test]
fn whitespace_only_buffer_is_zero_items_no_error() {
let c = ctx(" \n\t ", 3);
assert_eq!(c.position, None);
assert!(c.items.is_empty());
}
#[test]
fn out_of_range_offset_is_clamped_no_panic() {
let src = "Foo(x: 1)";
let c = ctx(src, 10_000);
let _ = c.items; }
#[test]
fn every_insert_text_parses_cleanly() {
for (src, offset) in [
("[]", 1),
("()", 1),
("{ }", 2),
("Point(x: 1, )", "Point(x: 1, )".len() - 1),
("{ alpha: 1, }", 2),
] {
let c = ctx(src, offset);
for item in &c.items {
let parsed = parse(&item.insert_text);
assert!(
parsed.diagnostics().is_empty(),
"insert_text {:?} (for {src:?}) must parse cleanly: {:?}",
item.insert_text,
parsed.diagnostics()
);
}
}
}
#[test]
fn completions_free_fn_returns_the_items() {
let c = ctx("[]", 1);
assert_eq!(completions(&c), c.items);
}