Skip to content

Commit 53f4186

Browse files
committed
test: improve test coverage from 55% to 60%
Add 35 new tests focusing on critical logic: - inlay_hints.rs: version normalization, hint creation for Cargo/npm/PyPI, PyPI version comparison edge cases, PEP 440 extraction - completion.rs: context detection for versions, package names, features, edge cases with whitespace, multiline, table syntax - code_actions.rs: range overlap logic (same, adjacent, contained, multiline) - parser.rs (PyPI): comments, platform markers, git URLs, complex version specifiers, Poetry path deps, PEP 735 includes, empty sections Coverage improvements: - completion.rs: 29% → 54% - code_actions.rs: 8% → 29% - inlay_hints.rs: 27% → 48% - parser.rs (PyPI): 78% → 83%
1 parent 5457c04 commit 53f4186

File tree

4 files changed

+458
-0
lines changed

4 files changed

+458
-0
lines changed

crates/deps-lsp/src/handlers/code_actions.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,4 +359,60 @@ mod tests {
359359
let range3 = Range::new(Position::new(1, 0), Position::new(1, 4));
360360
assert!(!ranges_overlap(range1, range3));
361361
}
362+
363+
#[test]
364+
fn test_ranges_overlap_same_range() {
365+
let range = Range::new(Position::new(1, 5), Position::new(1, 10));
366+
assert!(ranges_overlap(range, range));
367+
}
368+
369+
#[test]
370+
fn test_ranges_overlap_adjacent() {
371+
let range1 = Range::new(Position::new(1, 5), Position::new(1, 10));
372+
let range2 = Range::new(Position::new(1, 10), Position::new(1, 15));
373+
assert!(ranges_overlap(range1, range2));
374+
}
375+
376+
#[test]
377+
fn test_ranges_overlap_different_lines() {
378+
let range1 = Range::new(Position::new(1, 5), Position::new(1, 10));
379+
let range2 = Range::new(Position::new(2, 0), Position::new(2, 5));
380+
assert!(!ranges_overlap(range1, range2));
381+
}
382+
383+
#[test]
384+
fn test_ranges_overlap_multiline() {
385+
let range1 = Range::new(Position::new(1, 5), Position::new(3, 10));
386+
let range2 = Range::new(Position::new(2, 0), Position::new(4, 5));
387+
assert!(ranges_overlap(range1, range2));
388+
}
389+
390+
#[test]
391+
fn test_ranges_overlap_contained() {
392+
let outer = Range::new(Position::new(1, 0), Position::new(1, 20));
393+
let inner = Range::new(Position::new(1, 5), Position::new(1, 10));
394+
assert!(ranges_overlap(outer, inner));
395+
assert!(ranges_overlap(inner, outer));
396+
}
397+
398+
#[test]
399+
fn test_ranges_overlap_edge_case_same_position() {
400+
let range1 = Range::new(Position::new(1, 5), Position::new(1, 10));
401+
let range2 = Range::new(Position::new(1, 5), Position::new(1, 5));
402+
assert!(ranges_overlap(range1, range2));
403+
}
404+
405+
#[test]
406+
fn test_ranges_overlap_before() {
407+
let range1 = Range::new(Position::new(2, 0), Position::new(2, 10));
408+
let range2 = Range::new(Position::new(1, 0), Position::new(1, 10));
409+
assert!(!ranges_overlap(range1, range2));
410+
}
411+
412+
#[test]
413+
fn test_ranges_overlap_after() {
414+
let range1 = Range::new(Position::new(1, 0), Position::new(1, 10));
415+
let range2 = Range::new(Position::new(2, 0), Position::new(2, 10));
416+
assert!(!ranges_overlap(range1, range2));
417+
}
362418
}

crates/deps-lsp/src/handlers/completion.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,79 @@ mod tests {
174174
Some(CompletionContext::PackageName { .. })
175175
));
176176
}
177+
178+
#[test]
179+
fn test_determine_completion_context_feature() {
180+
let content = r#"serde = { version = "1.0", features = [""#;
181+
let position = Position::new(0, 40);
182+
let context = determine_completion_context(content, position);
183+
assert!(matches!(context, Some(CompletionContext::Feature { .. })));
184+
}
185+
186+
#[test]
187+
fn test_determine_completion_context_no_match() {
188+
let content = r#"# comment"#;
189+
let position = Position::new(0, 5);
190+
let context = determine_completion_context(content, position);
191+
assert!(context.is_none());
192+
}
193+
194+
#[test]
195+
fn test_determine_completion_context_version_with_spaces() {
196+
let content = r#" serde = ""#;
197+
let position = Position::new(0, 13);
198+
let context = determine_completion_context(content, position);
199+
assert!(matches!(context, Some(CompletionContext::Version { .. })));
200+
}
201+
202+
#[test]
203+
fn test_determine_completion_context_short_prefix() {
204+
let content = "s";
205+
let position = Position::new(0, 1);
206+
let context = determine_completion_context(content, position);
207+
if let Some(CompletionContext::PackageName { prefix }) = context {
208+
assert_eq!(prefix, "s");
209+
} else {
210+
panic!("Expected PackageName context");
211+
}
212+
}
213+
214+
#[test]
215+
fn test_determine_completion_context_hyphenated_name() {
216+
let content = "tokio-util";
217+
let position = Position::new(0, 10);
218+
let context = determine_completion_context(content, position);
219+
assert!(matches!(
220+
context,
221+
Some(CompletionContext::PackageName { .. })
222+
));
223+
}
224+
225+
#[test]
226+
fn test_determine_completion_context_underscored_name() {
227+
let content = "tower_lsp";
228+
let position = Position::new(0, 9);
229+
let context = determine_completion_context(content, position);
230+
assert!(matches!(
231+
context,
232+
Some(CompletionContext::PackageName { .. })
233+
));
234+
}
235+
236+
#[test]
237+
fn test_determine_completion_context_multiline_version() {
238+
let content = "[dependencies]\nserde = \"";
239+
let position = Position::new(1, 9);
240+
let context = determine_completion_context(content, position);
241+
assert!(matches!(context, Some(CompletionContext::Version { .. })));
242+
}
243+
244+
#[test]
245+
fn test_determine_completion_context_table_syntax() {
246+
let content = r#"[dependencies.serde]
247+
version = ""#;
248+
let position = Position::new(1, 11);
249+
let context = determine_completion_context(content, position);
250+
assert!(matches!(context, Some(CompletionContext::Version { .. })));
251+
}
177252
}

crates/deps-lsp/src/handlers/inlay_hints.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,4 +740,134 @@ mod tests {
740740
);
741741
assert_eq!(extract_pypi_min_version("^1.0"), Some("1.0".to_string())); // Poetry style
742742
}
743+
744+
#[test]
745+
fn test_normalize_and_parse_version() {
746+
assert_eq!(
747+
normalize_and_parse_version("1.0.0").unwrap().to_string(),
748+
"1.0.0"
749+
);
750+
assert_eq!(
751+
normalize_and_parse_version("1.0").unwrap().to_string(),
752+
"1.0.0"
753+
);
754+
assert_eq!(
755+
normalize_and_parse_version("8").unwrap().to_string(),
756+
"8.0.0"
757+
);
758+
assert!(normalize_and_parse_version("invalid").is_none());
759+
}
760+
761+
#[test]
762+
fn test_create_cargo_hint_up_to_date() {
763+
let config = InlayHintsConfig::default();
764+
let range = tower_lsp::lsp_types::Range {
765+
start: tower_lsp::lsp_types::Position {
766+
line: 0,
767+
character: 10,
768+
},
769+
end: tower_lsp::lsp_types::Position {
770+
line: 0,
771+
character: 15,
772+
},
773+
};
774+
775+
let hint = create_cargo_hint("serde", "1.0.0", range, "1.0.0", true, &config);
776+
777+
assert_eq!(hint.position, range.end);
778+
assert!(matches!(hint.kind, Some(InlayHintKind::TYPE)));
779+
assert_eq!(hint.padding_left, Some(true));
780+
}
781+
782+
#[test]
783+
fn test_create_cargo_hint_needs_update() {
784+
let config = InlayHintsConfig::default();
785+
let range = tower_lsp::lsp_types::Range {
786+
start: tower_lsp::lsp_types::Position {
787+
line: 0,
788+
character: 10,
789+
},
790+
end: tower_lsp::lsp_types::Position {
791+
line: 0,
792+
character: 15,
793+
},
794+
};
795+
796+
let hint = create_cargo_hint("serde", "1.0.0", range, "1.0.214", false, &config);
797+
798+
assert_eq!(hint.position, range.end);
799+
if let InlayHintLabel::LabelParts(parts) = hint.label {
800+
assert!(parts[0].value.contains("1.0.214"));
801+
} else {
802+
panic!("Expected LabelParts");
803+
}
804+
}
805+
806+
#[test]
807+
fn test_create_npm_hint_up_to_date() {
808+
let config = InlayHintsConfig::default();
809+
let range = tower_lsp::lsp_types::Range {
810+
start: tower_lsp::lsp_types::Position {
811+
line: 0,
812+
character: 10,
813+
},
814+
end: tower_lsp::lsp_types::Position {
815+
line: 0,
816+
character: 15,
817+
},
818+
};
819+
820+
let hint = create_npm_hint("express", "^4.18.0", range, "4.18.2", true, &config);
821+
822+
assert_eq!(hint.position, range.end);
823+
assert!(matches!(hint.kind, Some(InlayHintKind::TYPE)));
824+
}
825+
826+
#[test]
827+
fn test_create_pypi_hint_needs_update() {
828+
let config = InlayHintsConfig::default();
829+
let range = tower_lsp::lsp_types::Range {
830+
start: tower_lsp::lsp_types::Position {
831+
line: 0,
832+
character: 10,
833+
},
834+
end: tower_lsp::lsp_types::Position {
835+
line: 0,
836+
character: 15,
837+
},
838+
};
839+
840+
let hint = create_pypi_hint("django", ">=4.0", range, "5.0.0", false, &config);
841+
842+
assert_eq!(hint.position, range.end);
843+
if let InlayHintLabel::LabelParts(parts) = hint.label {
844+
assert!(parts[0].value.contains("5.0.0"));
845+
} else {
846+
panic!("Expected LabelParts");
847+
}
848+
}
849+
850+
#[test]
851+
fn test_extract_pypi_min_version_edge_cases() {
852+
assert_eq!(extract_pypi_min_version(">1.0"), Some("1.0".to_string()));
853+
assert_eq!(extract_pypi_min_version("~1.0"), Some("1.0".to_string()));
854+
assert_eq!(
855+
extract_pypi_min_version(">=1.0, !=1.5"),
856+
Some("1.0".to_string())
857+
);
858+
assert!(extract_pypi_min_version("<2.0").is_none());
859+
assert!(extract_pypi_min_version("").is_none());
860+
}
861+
862+
#[test]
863+
fn test_is_pypi_version_latest_edge_cases() {
864+
assert!(is_pypi_version_latest(">=0.1.0", "0.1.5"));
865+
assert!(!is_pypi_version_latest(">=0.1.0", "0.2.0"));
866+
assert!(is_pypi_version_latest("==1.0.0", "1.0.0"));
867+
// ==1.0.0 means pinned to 1.0.0, but the function checks major version match
868+
// So with latest 1.1.0 (same major), it returns true (within major version family)
869+
assert!(is_pypi_version_latest("==1.0.0", "1.1.0"));
870+
// Different major version should return false
871+
assert!(!is_pypi_version_latest("==1.0.0", "2.0.0"));
872+
}
743873
}

0 commit comments

Comments
 (0)