From c4cf92290282332a3b908c8a1b2787b5b76e950f Mon Sep 17 00:00:00 2001 From: "Andrei G." Date: Wed, 12 Aug 2026 22:43:40 +0200 Subject: [PATCH 1/3] fix(ci): split cross-compile checks into a dedicated matrix job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #109. The test job's matrix used os/rust as the matching keys for 'include' entries. Two include entries (x86_64-unknown-linux-musl and aarch64-unknown-linux-musl) both matched the same base (ubuntu-latest, stable) combination, so the second silently overwrote the first instead of creating a separate job — x86_64-unknown-linux-musl was never actually checked in CI despite the changelog claiming otherwise. Move the three build-only cross-compile checks (both musl targets plus aarch64-pc-windows-msvc) into a new cross-check job whose matrix is defined purely via include with no base os/rust axis to match against, so every entry unconditionally becomes its own job. This also lets the test job drop the now-unneeded cross/target conditionals it only carried to support the cross-compile entries. Also clarify the beta job's display name (Test (ubuntu-latest / beta) instead of the ambiguous Test (ubuntu-latest)). --- .github/workflows/ci.yml | 69 +++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92b549af..161e8975 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: run: cargo deny check test: - name: Test (${{ matrix.os }}${{ matrix.target && format(' / {0}', matrix.target) || '' }}) + name: Test (${{ matrix.os }}${{ matrix.rust != 'stable' && format(' / {0}', matrix.rust) || '' }}) needs: [changes, fmt, check] if: needs.changes.outputs.rust == 'true' || needs.changes.outputs.workflows == 'true' runs-on: ${{ matrix.os }} @@ -123,19 +123,47 @@ jobs: include: - os: ubuntu-latest rust: beta + + steps: + - uses: actions/checkout@v6 + + - uses: moonrepo/setup-rust@v1 + with: + channel: ${{ matrix.rust }} + bins: cargo-nextest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies (Ubuntu) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libssl-dev pkg-config + + - name: Run tests + run: cargo nextest run --workspace --all-features --no-fail-fast + + - name: Run doctests + run: cargo test --workspace --doc --all-features + + cross-check: + name: Cross-compile Check (${{ matrix.target }}) + needs: [changes, fmt, check] + if: needs.changes.outputs.rust == 'true' || needs.changes.outputs.workflows == 'true' + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: - os: windows-latest - rust: stable target: aarch64-pc-windows-msvc - cross: true + use_cross_tool: false - os: ubuntu-latest - rust: stable target: x86_64-unknown-linux-musl - cross: true use_cross_tool: true - os: ubuntu-latest - rust: stable target: aarch64-unknown-linux-musl - cross: true use_cross_tool: true steps: @@ -143,9 +171,7 @@ jobs: - uses: moonrepo/setup-rust@v1 with: - channel: ${{ matrix.rust }} targets: ${{ matrix.target }} - bins: ${{ !matrix.cross && 'cargo-nextest' || '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -159,16 +185,8 @@ jobs: if: matrix.use_cross_tool uses: taiki-e/install-action@cross - - name: Run tests - if: ${{ !matrix.cross }} - run: cargo nextest run --workspace --all-features --no-fail-fast - - - name: Run doctests - if: ${{ !matrix.cross }} - run: cargo test --workspace --doc --all-features - - name: Build for cross-compile target - if: ${{ matrix.cross && !matrix.use_cross_tool }} + if: ${{ !matrix.use_cross_tool }} run: cargo build --workspace --target ${{ matrix.target }} - name: Build for cross-compile target (musl via cross) @@ -300,7 +318,19 @@ jobs: ci-success: name: CI Success - needs: [changes, fmt, check, security, test, coverage, msrv, wasm, benchmark] + needs: + [ + changes, + fmt, + check, + security, + test, + cross-check, + coverage, + msrv, + wasm, + benchmark, + ] runs-on: ubuntu-latest if: always() steps: @@ -326,6 +356,7 @@ jobs: check_job "${{ needs.check.result }}" "check" || failed=1 check_job "${{ needs.security.result }}" "security" || failed=1 check_job "${{ needs.test.result }}" "test" || failed=1 + check_job "${{ needs.cross-check.result }}" "cross-check" || failed=1 check_job "${{ needs.coverage.result }}" "coverage" || failed=1 check_job "${{ needs.msrv.result }}" "msrv" || failed=1 check_job "${{ needs.wasm.result }}" "wasm" || failed=1 From fb700dc10cb7dbd927bfe6a919790348523a1b7b Mon Sep 17 00:00:00 2001 From: "Andrei G." Date: Wed, 12 Aug 2026 23:09:09 +0200 Subject: [PATCH 2/3] fix(tests): add cross-platform test URI helper for Windows compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing the CI matrix collision (previous commit) exposed that native Windows test runs had never actually executed before: the windows-latest/stable slot was always absorbed by the aarch64-pc-windows-msvc cross-compile include entry, which only builds, never runs cargo nextest. With native Windows tests now genuinely running, 247 tests failed across nearly every crate. All failures share one root cause: test fixtures build Uri values from Unix-style absolute path literals (e.g. "/home/user/project/test.toml") via Uri::from_file_path(...).unwrap(). Such a path has no drive letter, so it is not recognized as absolute on Windows, and from_file_path returns None, panicking the unwrap. Add deps_core::test_util::test_uri, a small helper that prefixes a synthetic C: drive on Windows before constructing the Uri and leaves the path untouched elsewhere. It is gated behind cfg(any(test, feature = "test-util")) and exposed to every other workspace crate via a test-util Cargo feature enabled in their dev-dependencies, so it's usable from their own test code without adding test-only surface to deps-core's normal public API. Switched every executed call site (unit tests, the deps-lsp integration test, and the one non-no_run doctest) to the helper. Non-executed occurrences — no_run doctests and benches/, which are only ever built, not run, in CI — were intentionally left alone. --- CHANGELOG.md | 4 ++ crates/deps-bundler/Cargo.toml | 1 + crates/deps-cargo/Cargo.toml | 1 + crates/deps-cargo/src/ecosystem.rs | 2 +- crates/deps-composer/Cargo.toml | 1 + crates/deps-composer/src/ecosystem.rs | 8 ++-- crates/deps-composer/src/parser.rs | 2 +- crates/deps-core/Cargo.toml | 6 +++ crates/deps-core/src/ecosystem_registry.rs | 4 +- crates/deps-core/src/lib.rs | 2 + crates/deps-core/src/lsp_helpers.rs | 44 +++++++++---------- crates/deps-core/src/macros.rs | 2 +- crates/deps-core/src/test_util.rs | 43 ++++++++++++++++++ crates/deps-dart/Cargo.toml | 1 + crates/deps-go/Cargo.toml | 1 + crates/deps-go/src/ecosystem.rs | 24 +++++----- crates/deps-gradle/Cargo.toml | 1 + crates/deps-gradle/src/ecosystem.rs | 4 +- crates/deps-gradle/src/parser/catalog.rs | 2 +- crates/deps-gradle/src/parser/groovy.rs | 2 +- crates/deps-gradle/src/parser/kotlin.rs | 2 +- crates/deps-gradle/src/parser/settings.rs | 2 +- crates/deps-lsp/Cargo.toml | 1 + crates/deps-lsp/src/document/lifecycle.rs | 42 ++++++++---------- crates/deps-lsp/src/document/loader.rs | 2 +- crates/deps-lsp/src/document/state.rs | 43 +++++++++--------- crates/deps-lsp/src/handlers/code_actions.rs | 10 ++--- crates/deps-lsp/src/handlers/completion.rs | 8 ++-- crates/deps-lsp/src/handlers/diagnostics.rs | 10 ++--- crates/deps-lsp/src/handlers/hover.rs | 10 ++--- crates/deps-lsp/src/handlers/inlay_hints.rs | 16 +++---- .../deps-lsp/tests/loading_indicator_e2e.rs | 11 +++-- crates/deps-maven/Cargo.toml | 1 + crates/deps-npm/Cargo.toml | 1 + crates/deps-npm/src/ecosystem.rs | 18 ++++---- crates/deps-npm/src/parser.rs | 2 +- crates/deps-pypi/Cargo.toml | 1 + crates/deps-pypi/src/ecosystem.rs | 18 ++++---- crates/deps-pypi/src/parser.rs | 2 +- crates/deps-swift/Cargo.toml | 1 + crates/deps-swift/src/ecosystem.rs | 4 +- crates/deps-swift/src/parser.rs | 2 +- 42 files changed, 210 insertions(+), 152 deletions(-) create mode 100644 crates/deps-core/src/test_util.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab2b61e..a5f38cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- **CI**: the `test` job's matrix `include` entries for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl` matched the same base `(ubuntu-latest, stable)` combination, so the second silently overwrote the first — `x86_64-unknown-linux-musl` was never actually checked in CI. Cross-compile checks (both musl targets plus `aarch64-pc-windows-msvc`) now run in a dedicated `cross-check` job with no shared base matrix axis, so `include` entries can no longer collide (resolves #109) +- **tests**: native Windows test runs were never exercised in CI (the `windows-latest`/stable slot was always absorbed by the `aarch64-pc-windows-msvc` cross-compile job above), which hid that most test fixtures built `Uri`s from Unix-style absolute paths — not recognized as absolute on Windows, causing `Uri::from_file_path(...).unwrap()` to panic. Added `deps_core::test_util::test_uri`, a cross-platform test helper (feature-gated via `test-util`), and switched all executed test/doctest call sites to it + ## [0.9.5] - 2026-08-12 ### Changed diff --git a/crates/deps-bundler/Cargo.toml b/crates/deps-bundler/Cargo.toml index 5e019845..b00d2d82 100644 --- a/crates/deps-bundler/Cargo.toml +++ b/crates/deps-bundler/Cargo.toml @@ -25,6 +25,7 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } criterion = { workspace = true, features = ["html_reports"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } diff --git a/crates/deps-cargo/Cargo.toml b/crates/deps-cargo/Cargo.toml index a485f391..9d620cca 100644 --- a/crates/deps-cargo/Cargo.toml +++ b/crates/deps-cargo/Cargo.toml @@ -26,6 +26,7 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } criterion = { workspace = true, features = ["html_reports"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } diff --git a/crates/deps-cargo/src/ecosystem.rs b/crates/deps-cargo/src/ecosystem.rs index e3c7661a..0475efa5 100644 --- a/crates/deps-cargo/src/ecosystem.rs +++ b/crates/deps-cargo/src/ecosystem.rs @@ -215,7 +215,7 @@ mod tests { fn uri(&self) -> &Uri { static URI: std::sync::LazyLock = - std::sync::LazyLock::new(|| Uri::from_file_path("/test/Cargo.toml").unwrap()); + std::sync::LazyLock::new(|| deps_core::test_util::test_uri("/test/Cargo.toml")); &URI } diff --git a/crates/deps-composer/Cargo.toml b/crates/deps-composer/Cargo.toml index b2d37f01..8091b79c 100644 --- a/crates/deps-composer/Cargo.toml +++ b/crates/deps-composer/Cargo.toml @@ -23,5 +23,6 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } tempfile = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/deps-composer/src/ecosystem.rs b/crates/deps-composer/src/ecosystem.rs index 57765b20..132ef05d 100644 --- a/crates/deps-composer/src/ecosystem.rs +++ b/crates/deps-composer/src/ecosystem.rs @@ -163,7 +163,7 @@ mod tests { async fn test_parse_manifest_valid() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = ComposerEcosystem::new(cache); - let uri = Uri::from_file_path("/test/composer.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/composer.json"); let content = r#"{"require": {"symfony/console": "^6.0"}}"#; let result = ecosystem.parse_manifest(content, &uri).await; @@ -177,7 +177,7 @@ mod tests { async fn test_parse_manifest_invalid() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = ComposerEcosystem::new(cache); - let uri = Uri::from_file_path("/test/composer.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/composer.json"); let result = ecosystem.parse_manifest("{invalid json}", &uri).await; assert!(result.is_err()); @@ -196,7 +196,7 @@ mod tests { async fn test_generate_inlay_hints_empty() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = ComposerEcosystem::new(cache); - let uri = Uri::from_file_path("/test/composer.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/composer.json"); let content = r#"{"require": {}}"#; let parse_result = ecosystem.parse_manifest(content, &uri).await.unwrap(); @@ -218,7 +218,7 @@ mod tests { async fn test_generate_completions_no_context() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = ComposerEcosystem::new(cache); - let uri = Uri::from_file_path("/test/composer.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/composer.json"); let content = r#"{"name": "test/project"}"#; let parse_result = ecosystem.parse_manifest(content, &uri).await.unwrap(); diff --git a/crates/deps-composer/src/parser.rs b/crates/deps-composer/src/parser.rs index 9b29c9fc..2ae8c381 100644 --- a/crates/deps-composer/src/parser.rs +++ b/crates/deps-composer/src/parser.rs @@ -242,7 +242,7 @@ mod tests { use super::*; fn test_uri() -> Uri { - Uri::from_file_path("/test/composer.json").unwrap() + deps_core::test_util::test_uri("/test/composer.json") } #[test] diff --git a/crates/deps-core/Cargo.toml b/crates/deps-core/Cargo.toml index 9aa0d935..5a6bfcb3 100644 --- a/crates/deps-core/Cargo.toml +++ b/crates/deps-core/Cargo.toml @@ -12,6 +12,12 @@ publish = true [lints] workspace = true +[features] +# Exposes `test_util` to other workspace crates' own test builds, where +# `cfg(test)` alone would not apply (deps-core is a normal, non-dev +# dependency there). +test-util = [] + [dependencies] async-trait = { workspace = true } bytes = { workspace = true } diff --git a/crates/deps-core/src/ecosystem_registry.rs b/crates/deps-core/src/ecosystem_registry.rs index 19ecc3d3..81cd3b0a 100644 --- a/crates/deps-core/src/ecosystem_registry.rs +++ b/crates/deps-core/src/ecosystem_registry.rs @@ -412,11 +412,11 @@ mod tests { registry.register(ecosystem); - let uri = Uri::from_file_path("/home/user/project/test.toml").unwrap(); + let uri = crate::test_util::test_uri("/home/user/project/test.toml"); let retrieved = registry.get_for_uri(&uri).unwrap(); assert_eq!(retrieved.id(), "test"); - let unknown_uri = Uri::from_file_path("/home/user/project/unknown.toml").unwrap(); + let unknown_uri = crate::test_util::test_uri("/home/user/project/unknown.toml"); assert!(registry.get_for_uri(&unknown_uri).is_none()); } diff --git a/crates/deps-core/src/lib.rs b/crates/deps-core/src/lib.rs index 99d97f78..01a0a853 100644 --- a/crates/deps-core/src/lib.rs +++ b/crates/deps-core/src/lib.rs @@ -20,6 +20,8 @@ pub mod lsp_helpers; pub mod macros; pub mod parser; pub mod registry; +#[cfg(any(test, feature = "test-util"))] +pub mod test_util; pub mod version_matcher; // Re-export commonly used types diff --git a/crates/deps-core/src/lsp_helpers.rs b/crates/deps-core/src/lsp_helpers.rs index 75bb5e86..eeb7d0b7 100644 --- a/crates/deps-core/src/lsp_helpers.rs +++ b/crates/deps-core/src/lsp_helpers.rs @@ -741,7 +741,7 @@ mod tests { #[test] fn test_inlay_hint_exact_version_shows_update_needed() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -759,7 +759,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -789,7 +789,7 @@ mod tests { #[test] fn test_inlay_hint_caret_version_up_to_date() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -807,7 +807,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -841,7 +841,7 @@ mod tests { #[test] fn test_loading_hint_shows_when_no_cached_version() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -859,7 +859,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let cached_versions = HashMap::new(); @@ -892,7 +892,7 @@ mod tests { #[test] fn test_loading_hint_disabled_when_config_false() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -910,7 +910,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let cached_versions = HashMap::new(); @@ -973,7 +973,7 @@ mod tests { #[test] fn test_loading_hint_not_shown_when_cached_version_exists() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -991,7 +991,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -1026,7 +1026,7 @@ mod tests { #[test] fn test_generate_diagnostics_from_cache_unknown_package() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; @@ -1037,7 +1037,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 11)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let cached_versions = HashMap::new(); @@ -1059,7 +1059,7 @@ mod tests { #[test] fn test_generate_diagnostics_from_cache_outdated_version() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; @@ -1070,7 +1070,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -1094,7 +1094,7 @@ mod tests { #[test] fn test_generate_diagnostics_from_cache_up_to_date() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; @@ -1105,7 +1105,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 5)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -1129,7 +1129,7 @@ mod tests { #[test] fn test_generate_diagnostics_from_cache_multiple_deps() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; @@ -1154,7 +1154,7 @@ mod tests { name_range: Range::new(Position::new(2, 0), Position::new(2, 7)), }, ], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -1186,7 +1186,7 @@ mod tests { #[test] fn test_inlay_hint_not_in_lockfile_but_satisfies_requirement() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -1204,7 +1204,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 9)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); @@ -1238,7 +1238,7 @@ mod tests { #[test] fn test_inlay_hint_not_in_lockfile_and_outdated() { use std::collections::HashMap; - use tower_lsp_server::ls_types::{Position, Range, Uri}; + use tower_lsp_server::ls_types::{Position, Range}; let formatter = MockFormatter; let config = EcosystemConfig { @@ -1256,7 +1256,7 @@ mod tests { version_range: Range::new(Position::new(0, 10), Position::new(0, 20)), name_range: Range::new(Position::new(0, 0), Position::new(0, 9)), }], - uri: Uri::from_file_path("/test/Cargo.toml").unwrap(), + uri: crate::test_util::test_uri("/test/Cargo.toml"), }; let mut cached_versions = HashMap::new(); diff --git a/crates/deps-core/src/macros.rs b/crates/deps-core/src/macros.rs index fdab5381..732c2bf3 100644 --- a/crates/deps-core/src/macros.rs +++ b/crates/deps-core/src/macros.rs @@ -453,7 +453,7 @@ mod tests { version_req: None, version_range: None, }], - uri: Uri::from_file_path("/test").unwrap(), + uri: crate::test_util::test_uri("/test"), }; assert_eq!(result.dependencies().len(), 1); diff --git a/crates/deps-core/src/test_util.rs b/crates/deps-core/src/test_util.rs new file mode 100644 index 00000000..6ca3277c --- /dev/null +++ b/crates/deps-core/src/test_util.rs @@ -0,0 +1,43 @@ +//! Cross-platform test URI helper shared across ecosystem crates. +//! +//! Test fixtures throughout the workspace write absolute paths in Unix +//! style (e.g. `/project/Cargo.toml`) for readability. `Uri::from_file_path` +//! requires a platform-absolute path, and a Unix-style path is not +//! recognized as absolute on Windows (no drive letter), so calling it +//! directly with such a literal panics on Windows only. [`test_uri`] +//! normalizes the path per host platform before constructing the [`Uri`]. + +use tower_lsp_server::ls_types::Uri; + +/// Builds a [`Uri`] from a Unix-style absolute test path. +/// +/// On Windows, a synthetic `C:` drive is prefixed so the path is +/// recognized as absolute; on other platforms the path is used as-is. +/// +/// # Panics +/// +/// Panics if the resulting path is not a valid file URI. This is a test +/// helper: fixture paths are expected to always be well-formed. +/// +/// # Examples +/// +/// ``` +/// use deps_core::test_util::test_uri; +/// +/// let uri = test_uri("/project/Cargo.toml"); +/// assert!(uri.path().as_str().ends_with("Cargo.toml")); +/// ``` +#[must_use] +pub fn test_uri(unix_path: &str) -> Uri { + #[cfg(windows)] + let owned; + #[cfg(windows)] + let path: &str = { + owned = format!("C:{unix_path}"); + &owned + }; + #[cfg(not(windows))] + let path: &str = unix_path; + + Uri::from_file_path(path).expect("test_uri: fixture path must be a valid file URI") +} diff --git a/crates/deps-dart/Cargo.toml b/crates/deps-dart/Cargo.toml index 894ce5d1..591eea10 100644 --- a/crates/deps-dart/Cargo.toml +++ b/crates/deps-dart/Cargo.toml @@ -25,6 +25,7 @@ urlencoding = { workspace = true } yaml-rust2 = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } criterion = { workspace = true, features = ["html_reports"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } diff --git a/crates/deps-go/Cargo.toml b/crates/deps-go/Cargo.toml index f3953881..7d479639 100644 --- a/crates/deps-go/Cargo.toml +++ b/crates/deps-go/Cargo.toml @@ -24,6 +24,7 @@ regex.workspace = true semver.workspace = true [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } tokio = { workspace = true, features = ["macros", "rt"] } tokio-test.workspace = true tempfile.workspace = true diff --git a/crates/deps-go/src/ecosystem.rs b/crates/deps-go/src/ecosystem.rs index 7cfb7cc4..b58d59c6 100644 --- a/crates/deps-go/src/ecosystem.rs +++ b/crates/deps-go/src/ecosystem.rs @@ -232,7 +232,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -276,7 +276,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -318,7 +318,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -361,7 +361,7 @@ mod tests { let mut dep = mock_dependency("github.com/gin-gonic/gin", Some("v1.9.1"), 5); dep.version_range = None; - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![dep], uri, @@ -465,7 +465,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -500,7 +500,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -531,7 +531,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -558,7 +558,7 @@ mod tests { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = GoEcosystem::new(cache); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![mock_dependency( "github.com/gin-gonic/gin", @@ -599,7 +599,7 @@ go 1.21 require github.com/ "; - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![], uri, @@ -625,7 +625,7 @@ require github.com/ go 1.21 "; - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let parse_result = MockParseResult { dependencies: vec![], uri, @@ -652,7 +652,7 @@ go 1.21 require github.com/gin-gonic/gin v1.9.1 "; - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let result = ecosystem.parse_manifest(content, &uri).await; assert!(result.is_ok()); @@ -671,7 +671,7 @@ require github.com/gin-gonic/gin v1.9.1 let ecosystem = GoEcosystem::new(cache); let content = ""; - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let result = ecosystem.parse_manifest(content, &uri).await; assert!(result.is_ok()); diff --git a/crates/deps-gradle/Cargo.toml b/crates/deps-gradle/Cargo.toml index f7d5983d..267a2e65 100644 --- a/crates/deps-gradle/Cargo.toml +++ b/crates/deps-gradle/Cargo.toml @@ -24,6 +24,7 @@ tower-lsp-server = { workspace = true } tracing = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/deps-gradle/src/ecosystem.rs b/crates/deps-gradle/src/ecosystem.rs index 49838c46..1601cda8 100644 --- a/crates/deps-gradle/src/ecosystem.rs +++ b/crates/deps-gradle/src/ecosystem.rs @@ -284,7 +284,7 @@ mod tests { async fn test_parse_manifest_kts() { let eco = GradleEcosystem::new(make_cache()); let content = "dependencies {\n implementation(\"junit:junit:4.13.2\")\n}\n"; - let uri = Uri::from_file_path("/project/build.gradle.kts").unwrap(); + let uri = deps_core::test_util::test_uri("/project/build.gradle.kts"); let result = eco.parse_manifest(content, &uri).await.unwrap(); assert_eq!(result.dependencies().len(), 1); } @@ -378,7 +378,7 @@ mod tests { async fn test_parse_manifest_groovy() { let eco = GradleEcosystem::new(make_cache()); let content = "dependencies {\n implementation 'junit:junit:4.13.2'\n}\n"; - let uri = Uri::from_file_path("/project/build.gradle").unwrap(); + let uri = deps_core::test_util::test_uri("/project/build.gradle"); let result = eco.parse_manifest(content, &uri).await.unwrap(); assert_eq!(result.dependencies().len(), 1); } diff --git a/crates/deps-gradle/src/parser/catalog.rs b/crates/deps-gradle/src/parser/catalog.rs index 5a2245ca..75db0dcd 100644 --- a/crates/deps-gradle/src/parser/catalog.rs +++ b/crates/deps-gradle/src/parser/catalog.rs @@ -145,7 +145,7 @@ mod tests { use super::*; fn make_uri() -> Uri { - Uri::from_file_path("/project/gradle/libs.versions.toml").unwrap() + deps_core::test_util::test_uri("/project/gradle/libs.versions.toml") } #[test] diff --git a/crates/deps-gradle/src/parser/groovy.rs b/crates/deps-gradle/src/parser/groovy.rs index cbd11c9a..7e26c3c4 100644 --- a/crates/deps-gradle/src/parser/groovy.rs +++ b/crates/deps-gradle/src/parser/groovy.rs @@ -219,7 +219,7 @@ mod tests { use super::*; fn make_uri() -> Uri { - Uri::from_file_path("/project/build.gradle").unwrap() + deps_core::test_util::test_uri("/project/build.gradle") } #[test] diff --git a/crates/deps-gradle/src/parser/kotlin.rs b/crates/deps-gradle/src/parser/kotlin.rs index 326cb15c..414b9f6a 100644 --- a/crates/deps-gradle/src/parser/kotlin.rs +++ b/crates/deps-gradle/src/parser/kotlin.rs @@ -154,7 +154,7 @@ mod tests { use super::*; fn make_uri() -> Uri { - Uri::from_file_path("/project/build.gradle.kts").unwrap() + deps_core::test_util::test_uri("/project/build.gradle.kts") } #[test] diff --git a/crates/deps-gradle/src/parser/settings.rs b/crates/deps-gradle/src/parser/settings.rs index fe888662..a52c4024 100644 --- a/crates/deps-gradle/src/parser/settings.rs +++ b/crates/deps-gradle/src/parser/settings.rs @@ -136,7 +136,7 @@ mod tests { use super::*; fn make_uri(name: &str) -> Uri { - Uri::from_file_path(format!("/project/{name}")).unwrap() + deps_core::test_util::test_uri(&format!("/project/{name}")) } #[test] diff --git a/crates/deps-lsp/Cargo.toml b/crates/deps-lsp/Cargo.toml index 1e218e9a..9ad2a4b1 100644 --- a/crates/deps-lsp/Cargo.toml +++ b/crates/deps-lsp/Cargo.toml @@ -59,6 +59,7 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } async-trait = { workspace = true } criterion = { workspace = true } insta = { workspace = true, features = ["json"] } diff --git a/crates/deps-lsp/src/document/lifecycle.rs b/crates/deps-lsp/src/document/lifecycle.rs index 5427ba1b..0e354b44 100644 --- a/crates/deps-lsp/src/document/lifecycle.rs +++ b/crates/deps-lsp/src/document/lifecycle.rs @@ -716,8 +716,7 @@ mod tests { #[test] fn test_ecosystem_registry_unknown_file() { let state = ServerState::new(); - let unknown_uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/unknown.txt").unwrap(); + let unknown_uri = deps_core::test_util::test_uri("/test/unknown.txt"); assert!(state.ecosystem_registry.get_for_uri(&unknown_uri).is_none()); } @@ -725,7 +724,7 @@ mod tests { async fn test_ensure_document_loaded_unsupported_file_check() { // Returns false for unknown file types (e.g., README.md) let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/README.md").unwrap(); + let uri = deps_core::test_util::test_uri("/test/README.md"); // Verify ecosystem registry correctly identifies unsupported files assert!( @@ -742,7 +741,7 @@ mod tests { // Test that load_document_from_disk fails gracefully for missing files use super::load_document_from_disk; - let uri = Uri::from_file_path("/nonexistent/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/nonexistent/Cargo.toml"); let result = load_document_from_disk(&uri).await; assert!(result.is_err(), "Should fail for missing files"); @@ -1218,15 +1217,14 @@ mod tests { #[test] fn test_ecosystem_registry_lookup() { let state = ServerState::new(); - let cargo_uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/Cargo.toml").unwrap(); + let cargo_uri = deps_core::test_util::test_uri("/test/Cargo.toml"); assert!(state.ecosystem_registry.get_for_uri(&cargo_uri).is_some()); } #[tokio::test] async fn test_document_parsing() { let state = Arc::new(ServerState::new()); - let uri = tower_lsp_server::ls_types::Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = r#"[dependencies] serde = "1.0" "#; @@ -1254,7 +1252,7 @@ serde = "1.0" #[tokio::test] async fn test_document_stored_even_when_parsing_fails() { let state = Arc::new(ServerState::new()); - let uri = tower_lsp_server::ls_types::Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); // Invalid TOML that will fail parsing let content = r#"[dependencies serde = "1.0" @@ -1301,7 +1299,7 @@ serde = "1.0" async fn test_ensure_document_loaded_fast_path() { // Fast path: document already loaded, should return true without loading let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = r#"[dependencies] serde = "1.0""#; @@ -1368,7 +1366,7 @@ serde = "1.0" async fn test_ensure_document_loaded_idempotent_check() { // Test that repeated loads are idempotent at the state level let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = r#"[dependencies] serde = "1.0""#; @@ -1407,16 +1405,14 @@ serde = "1.0""#; #[test] fn test_ecosystem_registry_lookup() { let state = ServerState::new(); - let npm_uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/package.json").unwrap(); + let npm_uri = deps_core::test_util::test_uri("/test/package.json"); assert!(state.ecosystem_registry.get_for_uri(&npm_uri).is_some()); } #[tokio::test] async fn test_document_parsing() { let state = Arc::new(ServerState::new()); - let uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"dependencies": {"express": "^4.18.0"}}"#; let ecosystem = state @@ -1447,16 +1443,14 @@ serde = "1.0""#; #[test] fn test_ecosystem_registry_lookup() { let state = ServerState::new(); - let pypi_uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/pyproject.toml").unwrap(); + let pypi_uri = deps_core::test_util::test_uri("/test/pyproject.toml"); assert!(state.ecosystem_registry.get_for_uri(&pypi_uri).is_some()); } #[tokio::test] async fn test_document_parsing() { let state = Arc::new(ServerState::new()); - let uri = - tower_lsp_server::ls_types::Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] dependencies = ["requests>=2.0.0"] "#; @@ -1489,14 +1483,14 @@ dependencies = ["requests>=2.0.0"] #[test] fn test_ecosystem_registry_lookup() { let state = ServerState::new(); - let go_uri = tower_lsp_server::ls_types::Uri::from_file_path("/test/go.mod").unwrap(); + let go_uri = deps_core::test_util::test_uri("/test/go.mod"); assert!(state.ecosystem_registry.get_for_uri(&go_uri).is_some()); } #[tokio::test] async fn test_document_parsing() { let state = Arc::new(ServerState::new()); - let uri = tower_lsp_server::ls_types::Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let content = r"module example.com/mymodule go 1.21 @@ -1532,7 +1526,7 @@ require github.com/gorilla/mux v1.8.0 #[tokio::test] async fn test_preserve_cached_versions_on_change() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); // Initial document with 2 dependencies let content1 = r#"[dependencies] @@ -1611,7 +1605,7 @@ tokio = "1.0" #[tokio::test] async fn test_first_open_has_empty_cache() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = r#"[dependencies] serde = "1.0" @@ -1635,7 +1629,7 @@ serde = "1.0" #[tokio::test] async fn test_preserve_cache_on_parse_failure() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); // Valid initial document let content1 = r#"[dependencies] @@ -1747,7 +1741,7 @@ serde = "1.0" #[tokio::test] async fn test_cache_pruned_on_dependency_removal() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); // Initial document with 3 dependencies let content1 = r#"[dependencies] diff --git a/crates/deps-lsp/src/document/loader.rs b/crates/deps-lsp/src/document/loader.rs index 910e0d25..83531fe4 100644 --- a/crates/deps-lsp/src/document/loader.rs +++ b/crates/deps-lsp/src/document/loader.rs @@ -178,7 +178,7 @@ mod tests { #[tokio::test] async fn test_load_nonexistent_file() { - let uri = Uri::from_file_path("/nonexistent/file/path.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/nonexistent/file/path.toml"); let result = load_document_from_disk(&uri).await; assert!(result.is_err()); diff --git a/crates/deps-lsp/src/document/state.rs b/crates/deps-lsp/src/document/state.rs index cd5a33f4..f69feabc 100644 --- a/crates/deps-lsp/src/document/state.rs +++ b/crates/deps-lsp/src/document/state.rs @@ -325,11 +325,10 @@ impl Clone for DocumentState { /// /// ``` /// use deps_lsp::document::ColdStartLimiter; -/// use tower_lsp_server::ls_types::Uri; /// use std::time::Duration; /// /// let limiter = ColdStartLimiter::new(Duration::from_millis(100)); -/// let uri = Uri::from_file_path("/test.toml").unwrap(); +/// let uri = deps_core::test_util::test_uri("/test.toml"); /// /// assert!(limiter.allow_cold_start(&uri)); /// assert!(!limiter.allow_cold_start(&uri)); // Rate limited @@ -890,7 +889,7 @@ mod tests { use tokio::sync::Barrier; let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/concurrent-loading-test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/concurrent-loading-test.toml"); let doc = DocumentState::new_without_parse_result("cargo", String::new()); state.update_document(uri.clone(), doc); @@ -1024,25 +1023,25 @@ mod tests { fn test_ecosystem_from_uri() { #[cfg(feature = "cargo")] { - let cargo_uri = Uri::from_file_path("/path/to/Cargo.toml").unwrap(); + let cargo_uri = deps_core::test_util::test_uri("/path/to/Cargo.toml"); assert_eq!(Ecosystem::from_uri(&cargo_uri), Some(Ecosystem::Cargo)); } #[cfg(feature = "npm")] { - let npm_uri = Uri::from_file_path("/path/to/package.json").unwrap(); + let npm_uri = deps_core::test_util::test_uri("/path/to/package.json"); assert_eq!(Ecosystem::from_uri(&npm_uri), Some(Ecosystem::Npm)); } #[cfg(feature = "pypi")] { - let pypi_uri = Uri::from_file_path("/path/to/pyproject.toml").unwrap(); + let pypi_uri = deps_core::test_util::test_uri("/path/to/pyproject.toml"); assert_eq!(Ecosystem::from_uri(&pypi_uri), Some(Ecosystem::Pypi)); } #[cfg(feature = "go")] { - let go_uri = Uri::from_file_path("/path/to/go.mod").unwrap(); + let go_uri = deps_core::test_util::test_uri("/path/to/go.mod"); assert_eq!(Ecosystem::from_uri(&go_uri), Some(Ecosystem::Go)); } - let unknown_uri = Uri::from_file_path("/path/to/README.md").unwrap(); + let unknown_uri = deps_core::test_util::test_uri("/path/to/README.md"); assert_eq!(Ecosystem::from_uri(&unknown_uri), None); } @@ -1070,7 +1069,7 @@ mod tests { #[tokio::test] async fn test_server_state_background_tasks() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); let task = tokio::spawn(async { tokio::time::sleep(std::time::Duration::from_millis(100)).await; @@ -1083,7 +1082,7 @@ mod tests { #[tokio::test] async fn test_spawn_background_task_cancels_previous() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); let task1 = tokio::spawn(async { tokio::time::sleep(std::time::Duration::from_secs(10)).await; @@ -1100,7 +1099,7 @@ mod tests { #[tokio::test] async fn test_cancel_background_task_nonexistent() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); state.cancel_background_task(&uri).await; } @@ -1115,7 +1114,7 @@ mod tests { #[test] fn test_allows_first_request() { let limiter = ColdStartLimiter::new(Duration::from_millis(100)); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); assert!( limiter.allow_cold_start(&uri), "First request should be allowed" @@ -1125,7 +1124,7 @@ mod tests { #[test] fn test_blocks_rapid_requests() { let limiter = ColdStartLimiter::new(Duration::from_millis(100)); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); assert!(limiter.allow_cold_start(&uri), "First request allowed"); assert!( @@ -1137,7 +1136,7 @@ mod tests { #[tokio::test] async fn test_allows_after_interval() { let limiter = ColdStartLimiter::new(Duration::from_millis(50)); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); assert!(limiter.allow_cold_start(&uri), "First request allowed"); tokio::time::sleep(Duration::from_millis(60)).await; @@ -1150,8 +1149,8 @@ mod tests { #[test] fn test_different_uris_independent() { let limiter = ColdStartLimiter::new(Duration::from_millis(100)); - let uri1 = Uri::from_file_path("/test1.toml").unwrap(); - let uri2 = Uri::from_file_path("/test2.toml").unwrap(); + let uri1 = deps_core::test_util::test_uri("/test1.toml"); + let uri2 = deps_core::test_util::test_uri("/test2.toml"); assert!(limiter.allow_cold_start(&uri1), "URI 1 first request"); assert!(limiter.allow_cold_start(&uri2), "URI 2 first request"); @@ -1168,8 +1167,8 @@ mod tests { #[test] fn test_cleanup() { let limiter = ColdStartLimiter::new(Duration::from_millis(100)); - let uri1 = Uri::from_file_path("/test1.toml").unwrap(); - let uri2 = Uri::from_file_path("/test2.toml").unwrap(); + let uri1 = deps_core::test_util::test_uri("/test1.toml"); + let uri2 = deps_core::test_util::test_uri("/test2.toml"); limiter.allow_cold_start(&uri1); limiter.allow_cold_start(&uri2); @@ -1188,7 +1187,7 @@ mod tests { use std::sync::Arc; let limiter = Arc::new(ColdStartLimiter::new(Duration::from_millis(100))); - let uri = Uri::from_file_path("/concurrent-test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/concurrent-test.toml"); let mut handles = vec![]; const CONCURRENT_TASKS: usize = 10; @@ -1275,7 +1274,7 @@ mod tests { #[test] fn test_server_state_document_operations() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test.toml"); let deps = vec![create_test_dependency()]; let doc_state = DocumentState::new(Ecosystem::Cargo, "test".into(), deps); @@ -1346,7 +1345,7 @@ mod tests { #[test] fn test_document_state_new_from_parse_result() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let ecosystem = state.ecosystem_registry.get("cargo").unwrap(); let content = "[dependencies]\nserde = \"1.0\"\n".to_string(); @@ -1662,7 +1661,7 @@ mod tests { #[test] fn test_document_state_new_from_parse_result() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test/go.mod").unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); let ecosystem = state.ecosystem_registry.get("go").unwrap(); let content = "module example.com/myapp\n\ngo 1.21\n\nrequire github.com/gin-gonic/gin v1.9.1\n" diff --git a/crates/deps-lsp/src/handlers/code_actions.rs b/crates/deps-lsp/src/handlers/code_actions.rs index 4ccbfc30..b5f47145 100644 --- a/crates/deps-lsp/src/handlers/code_actions.rs +++ b/crates/deps-lsp/src/handlers/code_actions.rs @@ -55,14 +55,14 @@ mod tests { use super::*; use crate::document::ServerState; use crate::test_utils::test_helpers::create_test_client_and_config; - use tower_lsp_server::ls_types::{Position, Range, TextDocumentIdentifier, Uri}; + use tower_lsp_server::ls_types::{Position, Range, TextDocumentIdentifier}; // Generic tests (no feature flag required) #[tokio::test] async fn test_handle_code_actions_missing_document() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let params = CodeActionParams { text_document: TextDocumentIdentifier { uri }, @@ -86,7 +86,7 @@ mod tests { #[tokio::test] async fn test_handle_code_actions() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let ecosystem = state.ecosystem_registry.get("cargo").unwrap(); let content = r#"[dependencies] @@ -118,7 +118,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_code_actions_no_parse_result() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]); state.update_document(uri.clone(), doc_state); @@ -146,7 +146,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_code_actions() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let ecosystem = state.ecosystem_registry.get("npm").unwrap(); let content = r#"{"dependencies": {"express": "4.0.0"}}"#.to_string(); diff --git a/crates/deps-lsp/src/handlers/completion.rs b/crates/deps-lsp/src/handlers/completion.rs index 672f4bad..c1d84fe3 100644 --- a/crates/deps-lsp/src/handlers/completion.rs +++ b/crates/deps-lsp/src/handlers/completion.rs @@ -325,13 +325,13 @@ mod tests { use crate::document::DocumentState; use crate::test_utils::test_helpers::create_test_client_and_config; use tower_lsp_server::ls_types::{ - Position, TextDocumentIdentifier, TextDocumentPositionParams, Uri, + Position, TextDocumentIdentifier, TextDocumentPositionParams, }; #[tokio::test] async fn test_completion_returns_empty_for_missing_document() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let params = CompletionParams { text_document_position: TextDocumentPositionParams { @@ -353,7 +353,7 @@ mod tests { #[tokio::test] async fn test_completion_delegates_to_ecosystem() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = "[dependencies]\nserde = \"1.0\"".to_string(); @@ -674,7 +674,7 @@ something use crate::document::Ecosystem; let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); // Malformed content that will fail to parse let content = r"[dependencies] diff --git a/crates/deps-lsp/src/handlers/diagnostics.rs b/crates/deps-lsp/src/handlers/diagnostics.rs index 202165a0..e6b02929 100644 --- a/crates/deps-lsp/src/handlers/diagnostics.rs +++ b/crates/deps-lsp/src/handlers/diagnostics.rs @@ -82,7 +82,7 @@ mod tests { #[tokio::test] async fn test_handle_diagnostics_missing_document() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = DiagnosticsConfig::default(); let (client, full_config) = create_test_client_and_config(); @@ -99,7 +99,7 @@ mod tests { #[tokio::test] async fn test_handle_diagnostics() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = DiagnosticsConfig::default(); let ecosystem = state.ecosystem_registry.get("cargo").unwrap(); @@ -124,7 +124,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_diagnostics_no_parse_result() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = DiagnosticsConfig::default(); let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]); @@ -145,7 +145,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_diagnostics() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let config = DiagnosticsConfig::default(); let ecosystem = state.ecosystem_registry.get("npm").unwrap(); @@ -174,7 +174,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_diagnostics() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let config = DiagnosticsConfig::default(); let ecosystem = state.ecosystem_registry.get("pypi").unwrap(); diff --git a/crates/deps-lsp/src/handlers/hover.rs b/crates/deps-lsp/src/handlers/hover.rs index 43c6fe29..47f1c2d2 100644 --- a/crates/deps-lsp/src/handlers/hover.rs +++ b/crates/deps-lsp/src/handlers/hover.rs @@ -45,7 +45,7 @@ mod tests { use crate::document::ServerState; use crate::test_utils::test_helpers::create_test_client_and_config; use tower_lsp_server::ls_types::{ - Position, TextDocumentIdentifier, TextDocumentPositionParams, Uri, + Position, TextDocumentIdentifier, TextDocumentPositionParams, }; // Generic tests (no feature flag required) @@ -53,7 +53,7 @@ mod tests { #[tokio::test] async fn test_handle_hover_missing_document() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let (client, config) = create_test_client_and_config(); let params = HoverParams { @@ -77,7 +77,7 @@ mod tests { #[tokio::test] async fn test_handle_hover() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let ecosystem = state.ecosystem_registry.get("cargo").unwrap(); let content = r#"[dependencies] @@ -109,7 +109,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_hover_no_parse_result() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]); state.update_document(uri.clone(), doc_state); @@ -137,7 +137,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_hover() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let ecosystem = state.ecosystem_registry.get("npm").unwrap(); let content = r#"{"dependencies": {"express": "4.0.0"}}"#.to_string(); diff --git a/crates/deps-lsp/src/handlers/inlay_hints.rs b/crates/deps-lsp/src/handlers/inlay_hints.rs index fe940df6..dbe88b0d 100644 --- a/crates/deps-lsp/src/handlers/inlay_hints.rs +++ b/crates/deps-lsp/src/handlers/inlay_hints.rs @@ -84,7 +84,7 @@ mod tests { use super::*; use crate::document::ServerState; use crate::test_utils::test_helpers::create_test_client_and_config; - use tower_lsp_server::ls_types::{TextDocumentIdentifier, Uri}; + use tower_lsp_server::ls_types::TextDocumentIdentifier; // Generic tests (no feature flag required) @@ -102,7 +102,7 @@ mod tests { #[tokio::test] async fn test_handle_inlay_hints_disabled_returns_empty() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = InlayHintsConfig { enabled: false, up_to_date_text: "✅".to_string(), @@ -126,7 +126,7 @@ mod tests { #[tokio::test] async fn test_handle_inlay_hints_missing_document() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "✅".to_string(), @@ -156,7 +156,7 @@ mod tests { #[tokio::test] async fn test_handle_inlay_hints() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "✅".to_string(), @@ -194,7 +194,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_inlay_hints_no_parse_result() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "✅".to_string(), @@ -221,7 +221,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_inlay_hints_custom_config() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "OK".to_string(), @@ -266,7 +266,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_inlay_hints() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "✅".to_string(), @@ -308,7 +308,7 @@ serde = "1.0.0" #[tokio::test] async fn test_handle_inlay_hints() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let config = InlayHintsConfig { enabled: true, up_to_date_text: "✅".to_string(), diff --git a/crates/deps-lsp/tests/loading_indicator_e2e.rs b/crates/deps-lsp/tests/loading_indicator_e2e.rs index 670ffb6d..aa880eda 100644 --- a/crates/deps-lsp/tests/loading_indicator_e2e.rs +++ b/crates/deps-lsp/tests/loading_indicator_e2e.rs @@ -7,14 +7,13 @@ use deps_lsp::config::{DepsConfig, LoadingIndicatorConfig}; use deps_lsp::document::{DocumentState, LoadingState, ServerState}; use std::sync::Arc; use std::time::Duration; -use tower_lsp_server::ls_types::Uri; /// Test loading state lifecycle for Cargo ecosystem. #[cfg(feature = "cargo")] #[tokio::test] async fn test_loading_state_lifecycle_cargo() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let content = r#"[dependencies] serde = "1.0.0" tokio = { version = "1.0", features = ["full"] } @@ -127,8 +126,8 @@ fn test_progress_only_mode() { async fn test_concurrent_loading_multiple_documents() { let state = Arc::new(ServerState::new()); - let uri1 = Uri::from_file_path("/test/Cargo1.toml").unwrap(); - let uri2 = Uri::from_file_path("/test/Cargo2.toml").unwrap(); + let uri1 = deps_core::test_util::test_uri("/test/Cargo1.toml"); + let uri2 = deps_core::test_util::test_uri("/test/Cargo2.toml"); let content = r#"[dependencies] serde = "1.0.0" @@ -371,7 +370,7 @@ fn test_combined_config() { #[test] fn test_server_state_document_has_loading_state() { let state = ServerState::new(); - let uri = Uri::from_file_path("/test/Cargo.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Cargo.toml"); let doc = DocumentState::new_without_parse_result("cargo", String::new()); @@ -453,7 +452,7 @@ async fn test_loading_timeout_scenario() { #[tokio::test] async fn test_rapid_set_loading_calls() { let state = Arc::new(ServerState::new()); - let uri = Uri::from_file_path("/test/rapid.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/rapid.toml"); let doc = DocumentState::new_without_parse_result("cargo", String::new()); state.update_document(uri.clone(), doc); diff --git a/crates/deps-maven/Cargo.toml b/crates/deps-maven/Cargo.toml index 685e7a0c..5dcfbb28 100644 --- a/crates/deps-maven/Cargo.toml +++ b/crates/deps-maven/Cargo.toml @@ -25,6 +25,7 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/deps-npm/Cargo.toml b/crates/deps-npm/Cargo.toml index e5e5cd40..e0251a93 100644 --- a/crates/deps-npm/Cargo.toml +++ b/crates/deps-npm/Cargo.toml @@ -25,6 +25,7 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } criterion = { workspace = true, features = ["html_reports"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } diff --git a/crates/deps-npm/src/ecosystem.rs b/crates/deps-npm/src/ecosystem.rs index 84fb4685..b9f1bb53 100644 --- a/crates/deps-npm/src/ecosystem.rs +++ b/crates/deps-npm/src/ecosystem.rs @@ -284,7 +284,7 @@ mod tests { async fn test_parse_manifest_valid_json() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"dependencies": {"express": "^4.18.0"}}"#; @@ -299,7 +299,7 @@ mod tests { async fn test_parse_manifest_invalid_json() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let invalid_content = r#"{"dependencies": invalid json"#; @@ -311,7 +311,7 @@ mod tests { async fn test_parse_manifest_empty_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"dependencies": {}}"#; @@ -344,7 +344,7 @@ mod tests { async fn test_generate_inlay_hints_empty_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"dependencies": {}}"#; @@ -370,7 +370,7 @@ mod tests { async fn test_generate_completions_no_context() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"name": "test"}"#; @@ -394,7 +394,7 @@ mod tests { // npm doesn't have features, so this should always return empty let content = r#"{"dependencies": {"express": "4.0.0"}}"#; - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let parse_result = ecosystem.parse_manifest(content, &uri).await.unwrap(); let position = Position { @@ -414,7 +414,7 @@ mod tests { async fn test_generate_hover_no_dependency_at_position() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"name": "test"}"#; @@ -442,7 +442,7 @@ mod tests { async fn test_generate_code_actions_no_actions() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"name": "test"}"#; @@ -464,7 +464,7 @@ mod tests { async fn test_generate_diagnostics_no_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = NpmEcosystem::new(cache); - let uri = Uri::from_file_path("/test/package.json").unwrap(); + let uri = deps_core::test_util::test_uri("/test/package.json"); let content = r#"{"dependencies": {}}"#; diff --git a/crates/deps-npm/src/parser.rs b/crates/deps-npm/src/parser.rs index 9fb496c2..c943fad4 100644 --- a/crates/deps-npm/src/parser.rs +++ b/crates/deps-npm/src/parser.rs @@ -257,7 +257,7 @@ mod tests { use super::*; fn test_uri() -> Uri { - Uri::from_file_path("/test/package.json").unwrap() + deps_core::test_util::test_uri("/test/package.json") } #[test] diff --git a/crates/deps-pypi/Cargo.toml b/crates/deps-pypi/Cargo.toml index 7a615658..e087a5f8 100644 --- a/crates/deps-pypi/Cargo.toml +++ b/crates/deps-pypi/Cargo.toml @@ -27,6 +27,7 @@ tracing = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } criterion = { workspace = true, features = ["html_reports"] } insta = { workspace = true, features = ["json"] } mockito = { workspace = true } diff --git a/crates/deps-pypi/src/ecosystem.rs b/crates/deps-pypi/src/ecosystem.rs index 0750e713..e111e219 100644 --- a/crates/deps-pypi/src/ecosystem.rs +++ b/crates/deps-pypi/src/ecosystem.rs @@ -289,7 +289,7 @@ mod tests { async fn test_parse_manifest_valid_content() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" @@ -307,7 +307,7 @@ dependencies = ["requests>=2.0.0"] async fn test_parse_manifest_invalid_toml() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let invalid_content = "[project\nname = invalid"; @@ -319,7 +319,7 @@ dependencies = ["requests>=2.0.0"] async fn test_parse_manifest_empty_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" @@ -355,7 +355,7 @@ dependencies = [] async fn test_generate_inlay_hints_empty_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r"[project] dependencies = [] @@ -383,7 +383,7 @@ dependencies = [] async fn test_generate_completions_no_context() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" @@ -413,7 +413,7 @@ name = "test" let content = r#"[project] dependencies = ["requests"] "#; - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let parse_result = ecosystem.parse_manifest(content, &uri).await.unwrap(); // Test with any position - feature context should return empty @@ -434,7 +434,7 @@ dependencies = ["requests"] async fn test_generate_hover_no_dependency_at_position() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" @@ -464,7 +464,7 @@ name = "test" async fn test_generate_code_actions_no_actions() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" @@ -488,7 +488,7 @@ name = "test" async fn test_generate_diagnostics_no_dependencies() { let cache = Arc::new(deps_core::HttpCache::new()); let ecosystem = PypiEcosystem::new(cache); - let uri = Uri::from_file_path("/test/pyproject.toml").unwrap(); + let uri = deps_core::test_util::test_uri("/test/pyproject.toml"); let content = r#"[project] name = "test" diff --git a/crates/deps-pypi/src/parser.rs b/crates/deps-pypi/src/parser.rs index 53e1192e..9c47d450 100644 --- a/crates/deps-pypi/src/parser.rs +++ b/crates/deps-pypi/src/parser.rs @@ -687,7 +687,7 @@ mod tests { use super::*; fn test_uri() -> Uri { - Uri::from_file_path("/test/pyproject.toml").unwrap() + deps_core::test_util::test_uri("/test/pyproject.toml") } #[test] diff --git a/crates/deps-swift/Cargo.toml b/crates/deps-swift/Cargo.toml index a860b58c..56ad8185 100644 --- a/crates/deps-swift/Cargo.toml +++ b/crates/deps-swift/Cargo.toml @@ -25,6 +25,7 @@ thiserror = { workspace = true } urlencoding = { workspace = true } [dev-dependencies] +deps-core = { workspace = true, features = ["test-util"] } insta = { workspace = true, features = ["json"] } tempfile = { workspace = true } tokio-test = { workspace = true } diff --git a/crates/deps-swift/src/ecosystem.rs b/crates/deps-swift/src/ecosystem.rs index e4bdce80..6fff5b92 100644 --- a/crates/deps-swift/src/ecosystem.rs +++ b/crates/deps-swift/src/ecosystem.rs @@ -177,7 +177,7 @@ mod tests { async fn test_parse_manifest_valid() { let cache = Arc::new(deps_core::HttpCache::new()); let eco = SwiftEcosystem::new(cache); - let uri = Uri::from_file_path("/test/Package.swift").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Package.swift"); let content = r#".package(url: "https://github.com/apple/swift-nio.git", from: "2.40.0")"#; let result = eco.parse_manifest(content, &uri).await; assert!(result.is_ok()); @@ -188,7 +188,7 @@ mod tests { async fn test_parse_manifest_empty() { let cache = Arc::new(deps_core::HttpCache::new()); let eco = SwiftEcosystem::new(cache); - let uri = Uri::from_file_path("/test/Package.swift").unwrap(); + let uri = deps_core::test_util::test_uri("/test/Package.swift"); let result = eco.parse_manifest("// empty file", &uri).await; assert!(result.is_ok()); assert!(result.unwrap().dependencies().is_empty()); diff --git a/crates/deps-swift/src/parser.rs b/crates/deps-swift/src/parser.rs index b512aa6e..f0f8a07f 100644 --- a/crates/deps-swift/src/parser.rs +++ b/crates/deps-swift/src/parser.rs @@ -493,7 +493,7 @@ mod tests { use deps_core::Dependency; fn test_uri() -> Uri { - Uri::from_file_path("/test/Package.swift").unwrap() + deps_core::test_util::test_uri("/test/Package.swift") } #[test] From b0683c9602d65b11d80ed12aced4626ba1b4e229 Mon Sep 17 00:00:00 2001 From: "Andrei G." Date: Wed, 12 Aug 2026 23:17:43 +0200 Subject: [PATCH 3/3] fix(tests): cover remaining Windows-incompatible URI patterns The previous commit missed a few cases my sed transform's literal-string regex couldn't match: - deps-gradle/src/parser/mod.rs and deps-gradle/tests/integration_tests.rs each have a local make_uri(path: &str) helper that takes the Unix-style literal from the caller rather than containing it inline; point both at the shared test_uri helper instead of building the Uri themselves. - deps-go/src/lockfile.rs held the literal in a named variable one line above the from_file_path call, so the string-literal regex didn't match the call site itself. - deps-lsp/tests/lsp_integration.rs's cold-start tests build a URI by hand via format!("file://{}", temp_file.path().display()), which is wrong on Windows: backslash-separated paths and a bare drive letter are not valid in a file:// URI, so the server failed to resolve the file and the affected requests errored out. Uri::from_file_path already handles this correctly, so use it instead of manual string formatting. --- crates/deps-go/src/lockfile.rs | 3 +-- crates/deps-gradle/src/parser/mod.rs | 2 +- crates/deps-gradle/tests/integration_tests.rs | 2 +- crates/deps-lsp/tests/lsp_integration.rs | 20 ++++++++++++++----- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/deps-go/src/lockfile.rs b/crates/deps-go/src/lockfile.rs index e7edb6a2..403d9293 100644 --- a/crates/deps-go/src/lockfile.rs +++ b/crates/deps-go/src/lockfile.rs @@ -253,8 +253,7 @@ github.com/pkg/errors v0.9.1 h1:hash2= #[test] fn test_lockfile_provider_trait() { let parser = GoSumParser; - let manifest_path = "/test/go.mod"; - let uri = Uri::from_file_path(manifest_path).unwrap(); + let uri = deps_core::test_util::test_uri("/test/go.mod"); // Just verify the trait methods are callable let _ = parser.locate_lockfile(&uri); diff --git a/crates/deps-gradle/src/parser/mod.rs b/crates/deps-gradle/src/parser/mod.rs index 8bfa6887..f5fc4a3f 100644 --- a/crates/deps-gradle/src/parser/mod.rs +++ b/crates/deps-gradle/src/parser/mod.rs @@ -152,7 +152,7 @@ mod tests { use super::*; fn make_uri(path: &str) -> Uri { - Uri::from_file_path(path).unwrap() + deps_core::test_util::test_uri(path) } #[test] diff --git a/crates/deps-gradle/tests/integration_tests.rs b/crates/deps-gradle/tests/integration_tests.rs index ae729cff..aef86b06 100644 --- a/crates/deps-gradle/tests/integration_tests.rs +++ b/crates/deps-gradle/tests/integration_tests.rs @@ -4,7 +4,7 @@ use deps_gradle::parser::{GradleParseResult, parse_gradle}; use tower_lsp_server::ls_types::Uri; fn make_uri(path: &str) -> Uri { - Uri::from_file_path(path).unwrap() + deps_core::test_util::test_uri(path) } // --- Version Catalog --- diff --git a/crates/deps-lsp/tests/lsp_integration.rs b/crates/deps-lsp/tests/lsp_integration.rs index b62c6a25..dd427630 100644 --- a/crates/deps-lsp/tests/lsp_integration.rs +++ b/crates/deps-lsp/tests/lsp_integration.rs @@ -315,7 +315,9 @@ serde = "" temp_file.write_all(content.as_bytes()).unwrap(); temp_file.flush().unwrap(); - let uri = format!("file://{}", temp_file.path().display()); + let uri = tower_lsp_server::ls_types::Uri::from_file_path(temp_file.path()) + .unwrap() + .to_string(); let mut client = LspClient::spawn(); client.initialize(); @@ -347,7 +349,9 @@ serde = "1.0" temp_file.write_all(content.as_bytes()).unwrap(); temp_file.flush().unwrap(); - let uri = format!("file://{}", temp_file.path().display()); + let uri = tower_lsp_server::ls_types::Uri::from_file_path(temp_file.path()) + .unwrap() + .to_string(); let mut client = LspClient::spawn(); client.initialize(); @@ -376,7 +380,9 @@ serde = "1.0" temp_file.write_all(content.as_bytes()).unwrap(); temp_file.flush().unwrap(); - let uri = format!("file://{}", temp_file.path().display()); + let uri = tower_lsp_server::ls_types::Uri::from_file_path(temp_file.path()) + .unwrap() + .to_string(); let mut client = LspClient::spawn(); client.initialize(); @@ -409,7 +415,9 @@ serde = "1.0" temp_file.write_all(content.as_bytes()).unwrap(); temp_file.flush().unwrap(); - let uri = format!("file://{}", temp_file.path().display()); + let uri = tower_lsp_server::ls_types::Uri::from_file_path(temp_file.path()) + .unwrap() + .to_string(); let mut client = LspClient::spawn(); client.initialize(); @@ -487,7 +495,9 @@ serde = "1.0" temp_file.write_all(content.as_bytes()).unwrap(); temp_file.flush().unwrap(); - let uri = format!("file://{}", temp_file.path().display()); + let uri = tower_lsp_server::ls_types::Uri::from_file_path(temp_file.path()) + .unwrap() + .to_string(); let mut client = LspClient::spawn(); client.initialize();