Skip to content

Commit 43fdb87

Browse files
authored
test: improve test coverage for edge cases (#740)
1 parent 85217e3 commit 43fdb87

File tree

10 files changed

+192
-0
lines changed

10 files changed

+192
-0
lines changed

src/cache/borrowed_path.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
path::Path,
66
};
77

8+
#[derive(Debug)]
89
pub struct BorrowedCachedPath<'a> {
910
pub hash: u64,
1011
pub path: &'a Path,

src/cache/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,40 @@ mod thread_local;
66

77
pub use cache_impl::Cache;
88
pub use cached_path::CachedPath;
9+
10+
#[cfg(test)]
11+
mod tests {
12+
use super::borrowed_path::BorrowedCachedPath;
13+
use super::cache_impl::Cache;
14+
use crate::FileSystem;
15+
use std::path::Path;
16+
17+
#[test]
18+
fn test_borrowed_cached_path_eq() {
19+
let path1 = Path::new("/foo/bar");
20+
let path2 = Path::new("/foo/bar");
21+
let path3 = Path::new("/foo/baz");
22+
23+
let borrowed1 = BorrowedCachedPath { hash: 1, path: path1 };
24+
let borrowed2 = BorrowedCachedPath { hash: 2, path: path2 };
25+
let borrowed3 = BorrowedCachedPath { hash: 1, path: path3 };
26+
27+
// Same path should be equal even with different hash
28+
assert_eq!(borrowed1, borrowed2);
29+
// Different path should not be equal even with same hash
30+
assert_ne!(borrowed1, borrowed3);
31+
}
32+
33+
#[test]
34+
fn test_cached_path_debug() {
35+
#[cfg(feature = "yarn_pnp")]
36+
let cache = Cache::new(crate::FileSystemOs::new(false));
37+
#[cfg(not(feature = "yarn_pnp"))]
38+
let cache = Cache::new(crate::FileSystemOs::new());
39+
40+
let path = cache.value(Path::new("/foo/bar"));
41+
let debug_str = format!("{path:?}");
42+
assert!(debug_str.contains("FsCachedPath"));
43+
assert!(debug_str.contains("path"));
44+
}
45+
}

src/error.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,20 @@ fn test_coverage() {
235235
assert_eq!(format!("{error:?}"), r#"Specifier(Empty("x"))"#);
236236
assert_eq!(error.clone(), error);
237237
}
238+
239+
#[test]
240+
fn test_circular_path_bufs_display() {
241+
use std::path::PathBuf;
242+
243+
let paths = vec![
244+
PathBuf::from("/foo/tsconfig.json"),
245+
PathBuf::from("/bar/tsconfig.json"),
246+
PathBuf::from("/baz/tsconfig.json"),
247+
];
248+
let circular = CircularPathBufs::from(paths);
249+
let display_str = format!("{circular}");
250+
assert!(display_str.contains("/foo/tsconfig.json"));
251+
assert!(display_str.contains(" -> "));
252+
assert!(display_str.contains("/bar/tsconfig.json"));
253+
assert!(display_str.contains("/baz/tsconfig.json"));
254+
}

src/file_system.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,21 @@ fn metadata() {
337337
);
338338
let _ = meta;
339339
}
340+
341+
#[test]
342+
fn file_metadata_getters() {
343+
let file_meta = FileMetadata::new(true, false, false);
344+
assert!(file_meta.is_file());
345+
assert!(!file_meta.is_dir());
346+
assert!(!file_meta.is_symlink());
347+
348+
let dir_meta = FileMetadata::new(false, true, false);
349+
assert!(!dir_meta.is_file());
350+
assert!(dir_meta.is_dir());
351+
assert!(!dir_meta.is_symlink());
352+
353+
let symlink_meta = FileMetadata::new(false, false, true);
354+
assert!(!symlink_meta.is_file());
355+
assert!(!symlink_meta.is_dir());
356+
assert!(symlink_meta.is_symlink());
357+
}

src/tests/alias.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,20 @@ fn alias_try_fragment_as_path() {
299299
let resolution = resolver.resolve(&f, "#/a").map(|r| r.full_path());
300300
assert_eq!(resolution, Ok(f.join("#").join("a.js")));
301301
}
302+
303+
#[test]
304+
fn alias_with_multiple_fallbacks() {
305+
let f = super::fixture();
306+
let resolver = Resolver::new(ResolveOptions {
307+
alias: vec![(
308+
"multi".to_string(),
309+
vec![
310+
AliasValue::Path(f.join("nonexistent").to_string_lossy().to_string()),
311+
AliasValue::Path(f.join("foo").to_string_lossy().to_string()),
312+
],
313+
)],
314+
..ResolveOptions::default()
315+
});
316+
let resolution = resolver.resolve(&f, "multi/index.js").map(|r| r.full_path());
317+
assert_eq!(resolution, Ok(f.join("foo/index.js")));
318+
}

src/tests/extensions.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,24 @@ fn without_leading_dot() {
126126
..ResolveOptions::default()
127127
});
128128
}
129+
130+
#[test]
131+
fn extension_combinations() {
132+
let f = super::fixture().join("extensions");
133+
134+
let resolver = Resolver::new(ResolveOptions {
135+
extensions: vec![".jsx".into(), ".tsx".into(), ".js".into(), ".ts".into()],
136+
..ResolveOptions::default()
137+
});
138+
139+
let pass = [
140+
("should resolve file with explicit extension", "./foo.ts", "foo.ts"),
141+
("should resolve directory index", "./dir/index.ts", "dir/index.ts"),
142+
];
143+
144+
for (comment, request, expected_path) in pass {
145+
let resolved_path = resolver.resolve(&f, request).map(|r| r.full_path());
146+
let expected = f.join(expected_path);
147+
assert_eq!(resolved_path, Ok(expected), "{comment} {request} {expected_path}");
148+
}
149+
}

src/tests/package_json.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,19 @@ fn adjacent_to_node_modules() {
4646
assert_eq!(package_json_path, Some(&resolved_package_json_path));
4747
assert_eq!(package_json_name, Some("misc"));
4848
}
49+
50+
#[test]
51+
fn package_json_with_symlinks_true() {
52+
use crate::ResolveOptions;
53+
54+
let f = super::fixture_root().join("misc");
55+
let resolver = Resolver::new(ResolveOptions { symlinks: true, ..ResolveOptions::default() });
56+
57+
let path = f.join("dir-with-index");
58+
let request = "./index.js";
59+
let resolved_package_json_path = f.join("package.json");
60+
61+
let package_json = resolver.resolve(&path, request).unwrap().package_json().cloned();
62+
let package_json_path = package_json.as_ref().map(|p| &p.path);
63+
assert_eq!(package_json_path, Some(&resolved_package_json_path));
64+
}

src/tests/resolve.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ fn prefer_file_over_dir() {
138138
}
139139
}
140140

141+
#[test]
142+
fn resolve_edge_cases() {
143+
let f = super::fixture();
144+
let resolver = Resolver::default();
145+
146+
// Test various edge cases for path resolution
147+
let data = [("resolve with multiple dots", f.clone(), "./a/../main1.js", f.join("main1.js"))];
148+
149+
for (comment, path, request, expected) in data {
150+
let resolved_path = resolver.resolve(&path, request).map(|r| r.full_path());
151+
assert_eq!(resolved_path, Ok(expected), "{comment} {path:?} {request}");
152+
}
153+
}
154+
141155
#[test]
142156
fn resolve_dot() {
143157
let f = super::fixture_root().join("dot");

src/tests/symlink.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,36 @@ fn test_unsupported_targets() {
200200
Err(ResolveError::PathNotSupported(dos_device_temp_path))
201201
);
202202
}
203+
204+
#[test]
205+
fn test_circular_symlink() {
206+
let Some(SymlinkFixturePaths { root: _, temp_path }) =
207+
prepare_symlinks("temp.test_circular_symlink").unwrap()
208+
else {
209+
return;
210+
};
211+
212+
// Create a circular symlink: link1 -> link2 -> link1
213+
let link1_path = temp_path.join("link1");
214+
let link2_path = temp_path.join("link2");
215+
216+
if symlink(&link2_path, &link1_path, FileType::File).is_err() {
217+
// Skip test if we can't create symlinks
218+
return;
219+
}
220+
if symlink(&link1_path, &link2_path, FileType::File).is_err() {
221+
// Skip test if we can't create symlinks
222+
_ = fs::remove_file(&link1_path);
223+
return;
224+
}
225+
226+
let resolver = Resolver::default();
227+
let result = resolver.resolve(&temp_path, "./link1");
228+
229+
// Should error due to circular symlink
230+
assert!(result.is_err());
231+
232+
// Cleanup
233+
_ = fs::remove_file(&link1_path);
234+
_ = fs::remove_file(&link2_path);
235+
}

src/tests/tsconfig_extends.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,24 @@ fn test_extend_tsconfig_template_variables() {
9595
assert_eq!(resolved_path, Ok(f.join("src/utils.ts")));
9696
}
9797

98+
#[test]
99+
fn test_extend_tsconfig_missing_file() {
100+
use crate::ResolveError;
101+
102+
let f = super::fixture_root().join("tsconfig/cases");
103+
104+
let resolver = Resolver::new(ResolveOptions {
105+
tsconfig: Some(TsconfigOptions {
106+
config_file: f.join("nonexistent-tsconfig.json"),
107+
references: TsconfigReferences::Auto,
108+
}),
109+
..ResolveOptions::default()
110+
});
111+
112+
let result = resolver.resolve_tsconfig(&f);
113+
assert!(matches!(result, Err(ResolveError::TsconfigNotFound(_))));
114+
}
115+
98116
#[test]
99117
fn test_extend_tsconfig_multiple_inheritance() {
100118
let f = super::fixture_root().join("tsconfig/cases/extends-chain");

0 commit comments

Comments
 (0)