From d6cfd81416d5e6f78b0911cd31ee4d7e02acc2b8 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 18 Jun 2026 17:10:36 +0100 Subject: [PATCH 1/3] Use Packse server in check command tests --- crates/uv/tests/project/check.rs | 222 +++++++++++++++---------------- 1 file changed, 110 insertions(+), 112 deletions(-) diff --git a/crates/uv/tests/project/check.rs b/crates/uv/tests/project/check.rs index bb22bf9d464..b492fcb109f 100644 --- a/crates/uv/tests/project/check.rs +++ b/crates/uv/tests/project/check.rs @@ -1,11 +1,11 @@ use anyhow::Result; -#[cfg(feature = "test-pypi")] use assert_cmd::assert::OutputAssertExt; use assert_fs::prelude::*; use indoc::indoc; use insta::assert_snapshot; use uv_static::EnvVars; +use uv_test::packse::PackseServer; use uv_test::{diff_snapshot, uv_snapshot}; #[test] @@ -41,6 +41,7 @@ fn check_project() -> Result<()> { #[test] fn check_no_sync_creates_lock_without_sync() -> Result<()> { + let server = PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); context @@ -51,7 +52,7 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig==2.0.0"] + dependencies = ["a==1.2.3"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -62,8 +63,9 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { context .check() .arg("--no-sync") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -85,16 +87,13 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { revision = 3 requires-python = ">=3.12" - [options] - exclude-newer = "2026-02-15T00:00:00Z" - [[package]] - name = "iniconfig" - version = "2.0.0" - source = { registry = "https://pypi.org/simple" } - sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } + name = "a" + version = "1.2.3+foo" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+foo.tar.gz", hash = "sha256:d8c42806a0bf7b47451778ed2a909a2f9d22fe3a4bac673934fc8fb2537a886a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, + { url = "http://[LOCALHOST]/files/a-1.2.3+foo-py3-none-any.whl", hash = "sha256:2fd8eec176cad72b6c4372682485914aff65d215fb8cd71c85e9ed4511fa8bbe", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -102,14 +101,14 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "iniconfig" }, + { name = "a" }, ] [package.metadata] - requires-dist = [{ name = "iniconfig", specifier = "==2.0.0" }] + requires-dist = [{ name = "a", specifier = "==1.2.3" }] "#); }); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); Ok(()) } @@ -145,8 +144,6 @@ fn check_no_sync_uses_compatible_lock_interpreter() -> Result<()> { .arg("--no-sync") .arg("--python") .arg("3.11") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") .arg("--ty-version") .arg("0.0.17"), @" @@ -172,6 +169,7 @@ fn check_no_sync_uses_compatible_lock_interpreter() -> Result<()> { #[test] fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { + let server = PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -180,12 +178,13 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["anyio==3.7.0"] + dependencies = ["a==1.2.3+bar"] "#})?; context .lock() - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -195,7 +194,7 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig==2.0.0"] + dependencies = ["a==1.2.3+foo"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -206,8 +205,9 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { context .check() .arg("--no-sync") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -229,38 +229,21 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { assert_snapshot!(diff, @r#" --- old +++ new - @@ -1,48 +1,26 @@ + @@ -1,23 +1,23 @@ version = 1 revision = 3 requires-python = ">=3.12" - [options] - exclude-newer = "2026-02-15T00:00:00Z" - [[package]] - -name = "anyio" - -version = "3.7.0" - +name = "iniconfig" - +version = "2.0.0" - source = { registry = "https://pypi.org/simple" } - -dependencies = [ - - { name = "idna" }, - - { name = "sniffio" }, - -] - -sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce", size = 142737, upload-time = "2023-05-27T11:12:46.688Z" } - +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } + name = "a" + -version = "1.2.3+bar" + +version = "1.2.3+foo" + source = { registry = "http://[LOCALHOST]/simple/" } + -sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+bar.tar.gz", hash = "sha256:b96640078f7011764f0e428c0481c726149e4ae3190d29f9f5f7c2372e11f074", upload-time = "2024-03-24T00:00:00Z" } + +sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+foo.tar.gz", hash = "sha256:d8c42806a0bf7b47451778ed2a909a2f9d22fe3a4bac673934fc8fb2537a886a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - - { url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0", size = 80873, upload-time = "2023-05-27T11:12:44.474Z" }, - -] - - - -[[package]] - -name = "idna" - -version = "3.11" - -source = { registry = "https://pypi.org/simple" } - -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } - -wheels = [ - - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, - + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, + - { url = "http://[LOCALHOST]/files/a-1.2.3+bar-py3-none-any.whl", hash = "sha256:dc4f0c4f8461a32a72175670fa502b7bfb6c0e0ab542e8e26895caf77fc4b17d", upload-time = "2024-03-24T00:00:00Z" }, + + { url = "http://[LOCALHOST]/files/a-1.2.3+foo-py3-none-any.whl", hash = "sha256:2fd8eec176cad72b6c4372682485914aff65d215fb8cd71c85e9ed4511fa8bbe", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -268,31 +251,22 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - - { name = "anyio" }, - + { name = "iniconfig" }, + { name = "a" }, ] [package.metadata] - -requires-dist = [{ name = "anyio", specifier = "==3.7.0" }] - - - -[[package]] - -name = "sniffio" - -version = "1.3.1" - -source = { registry = "https://pypi.org/simple" } - -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } - -wheels = [ - - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, - -] - +requires-dist = [{ name = "iniconfig", specifier = "==2.0.0" }] + -requires-dist = [{ name = "a", specifier = "==1.2.3+bar" }] + +requires-dist = [{ name = "a", specifier = "==1.2.3+foo" }] "#); }); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); Ok(()) } #[test] fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { + let server = PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -301,12 +275,13 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["anyio==3.7.0"] + dependencies = ["a==1.2.3+bar"] "#})?; context .lock() - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -316,7 +291,7 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig==2.0.0"] + dependencies = ["a==1.2.3+foo"] "#})?; uv_snapshot!( @@ -325,8 +300,9 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { .check() .arg("--no-sync") .arg("--locked") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z"), + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER), @" success: false exit_code: 2 @@ -339,7 +315,7 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { ); assert_eq!(stale_lock, context.read("uv.lock")); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); Ok(()) } @@ -380,6 +356,7 @@ fn check_no_sync_locked_requires_existing_lock() -> Result<()> { #[test] fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { + let server = PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -388,12 +365,13 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["anyio==3.7.0"] + dependencies = ["a==1.2.3+bar"] "#})?; context .lock() - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -403,7 +381,7 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig==2.0.0"] + dependencies = ["a==1.2.3+foo"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -415,8 +393,9 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { .check() .arg("--no-sync") .arg("--frozen") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -431,7 +410,7 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { ); assert_eq!(stale_lock, context.read("uv.lock")); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); Ok(()) } @@ -472,6 +451,7 @@ fn check_no_sync_frozen_requires_existing_lock() -> Result<()> { #[test] fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { + let server = PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); context @@ -482,7 +462,7 @@ fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig==2.0.0"] + dependencies = ["a==1.2.3"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -494,8 +474,9 @@ fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { .check() .arg("--no-sync") .arg("--isolated") - .arg("--exclude-newer") - .arg("2026-02-15T00:00:00Z") + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -510,7 +491,7 @@ fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { ); assert!(!context.temp_dir.child("uv.lock").exists()); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); Ok(()) } @@ -947,8 +928,8 @@ fn check_type_error() -> Result<()> { } #[test] -#[cfg(feature = "test-pypi")] fn check_with_declared_dependency() -> Result<()> { + let server = PackseServer::new("extras/extra-does-not-exist-backtrack.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -957,19 +938,25 @@ fn check_with_declared_dependency() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig"] + dependencies = ["a==3.0.0"] [project.optional-dependencies] - test = ["typing-extensions"] + test = ["b==1.0.0"] "#})?; let main_py = context.temp_dir.child("main.py"); main_py.write_str(indoc! {r" - import iniconfig + import a "})?; // ty should resolve the import via the synced virtual environment. - uv_snapshot!(context.filters(), context.check(), @" + uv_snapshot!( + context.filters(), + context + .check() + .arg("--index") + .arg(server.index_url()), + @" success: true exit_code: 0 ----- stdout ----- @@ -978,26 +965,22 @@ fn check_with_declared_dependency() -> Result<()> { ----- stderr ----- warning: `uv check` is experimental and may change without warning. Pass `--preview-features check-command` to disable this warning. Installed 1 package in [TIME] - "); + " + ); context .assert_command( - "from importlib.metadata import distribution; assert distribution('iniconfig').read_text('INSTALLER') == 'uv'", + "from importlib.metadata import distribution; assert distribution('a').read_text('INSTALLER') == 'uv'", ) .success(); - assert!( - !context - .site_packages() - .join("typing_extensions.py") - .exists() - ); + assert!(!context.site_packages().join("b").exists()); Ok(()) } #[test] -#[cfg(feature = "test-pypi")] fn check_isolated() -> Result<()> { + let server = PackseServer::new("extras/extra-does-not-exist-backtrack.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -1006,18 +989,25 @@ fn check_isolated() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig"] + dependencies = ["a==3.0.0"] "#})?; let main_py = context.temp_dir.child("main.py"); main_py.write_str(indoc! {r" - import iniconfig + import a "})?; let environment_sentinel = context.venv.child("sentinel"); environment_sentinel.write_str("present")?; - uv_snapshot!(context.filters(), context.check().arg("--isolated"), @" + uv_snapshot!( + context.filters(), + context + .check() + .arg("--isolated") + .arg("--index") + .arg(server.index_url()), + @" success: true exit_code: 0 ----- stdout ----- @@ -1026,36 +1016,44 @@ fn check_isolated() -> Result<()> { ----- stderr ----- warning: `uv check` is experimental and may change without warning. Pass `--preview-features check-command` to disable this warning. Installed 1 package in [TIME] - "); + " + ); assert!(!context.temp_dir.child("uv.lock").exists()); - assert!(!context.site_packages().join("iniconfig").exists()); + assert!(!context.site_packages().join("a").exists()); assert!(environment_sentinel.exists()); // An existing lockfile should not be updated. - context.lock().assert().success(); + context + .lock() + .arg("--index") + .arg(server.index_url()) + .env_remove(EnvVars::UV_EXCLUDE_NEWER) + .assert() + .success(); let existing_lock = context.read("uv.lock"); pyproject_toml.write_str(indoc! {r#" [project] name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["iniconfig", "typing-extensions"] + dependencies = ["a==3.0.0", "b==1.0.0"] "#})?; main_py.write_str(indoc! {r" - import iniconfig - import typing_extensions + import a + import b "})?; - context.check().arg("--isolated").assert().success(); + context + .check() + .arg("--isolated") + .arg("--index") + .arg(server.index_url()) + .assert() + .success(); assert_eq!(existing_lock, context.read("uv.lock")); - assert!(!context.site_packages().join("iniconfig").exists()); - assert!( - !context - .site_packages() - .join("typing_extensions.py") - .exists() - ); + assert!(!context.site_packages().join("a").exists()); + assert!(!context.site_packages().join("b").exists()); assert!(environment_sentinel.exists()); Ok(()) From aa6ce2f82e172ba2557d23f027188762f30f78c2 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 18 Jun 2026 19:06:47 +0100 Subject: [PATCH 2/3] Add single-package Packse scenario for check tests --- crates/uv/tests/pip/pip_install_scenarios.rs | 37 +++++++++++++++ crates/uv/tests/project/check.rs | 50 ++++++++++---------- test/scenarios/simple/single-package.toml | 16 +++++++ 3 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 test/scenarios/simple/single-package.toml diff --git a/crates/uv/tests/pip/pip_install_scenarios.rs b/crates/uv/tests/pip/pip_install_scenarios.rs index ff613abf6c5..16658e2d7cc 100644 --- a/crates/uv/tests/pip/pip_install_scenarios.rs +++ b/crates/uv/tests/pip/pip_install_scenarios.rs @@ -3409,6 +3409,43 @@ fn python_version_does_not_exist() { context.assert_not_installed("a"); } +/// A single package with two stable versions. +/// +/// ```text +/// single-package +/// ├── environment +/// │ └── python3.12 +/// ├── root +/// │ └── requires a +/// │ ├── satisfied by a-1.0.0 +/// │ └── satisfied by a-2.0.0 +/// └── a +/// ├── a-1.0.0 +/// └── a-2.0.0 +/// ``` +#[test] +fn single_package() { + let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("simple/single-package.toml"); + + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + , @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + a==2.0.0 + "); + + // The latest version of 'a' should be selected. + context.assert_installed("a", "2.0.0"); +} + /// Both wheels and source distributions are available, and the user has disabled binaries. /// /// ```text diff --git a/crates/uv/tests/project/check.rs b/crates/uv/tests/project/check.rs index b492fcb109f..3ce99d7867a 100644 --- a/crates/uv/tests/project/check.rs +++ b/crates/uv/tests/project/check.rs @@ -41,7 +41,7 @@ fn check_project() -> Result<()> { #[test] fn check_no_sync_creates_lock_without_sync() -> Result<()> { - let server = PackseServer::new("local/local-simple.toml"); + let server = PackseServer::new("simple/single-package.toml"); let context = uv_test::test_context!("3.12"); context @@ -52,7 +52,7 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3"] + dependencies = ["a==1.0.0"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -89,11 +89,11 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { [[package]] name = "a" - version = "1.2.3+foo" + version = "1.0.0" source = { registry = "http://[LOCALHOST]/simple/" } - sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+foo.tar.gz", hash = "sha256:d8c42806a0bf7b47451778ed2a909a2f9d22fe3a4bac673934fc8fb2537a886a", upload-time = "2024-03-24T00:00:00Z" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "http://[LOCALHOST]/files/a-1.2.3+foo-py3-none-any.whl", hash = "sha256:2fd8eec176cad72b6c4372682485914aff65d215fb8cd71c85e9ed4511fa8bbe", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -105,7 +105,7 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { ] [package.metadata] - requires-dist = [{ name = "a", specifier = "==1.2.3" }] + requires-dist = [{ name = "a", specifier = "==1.0.0" }] "#); }); assert!(!context.site_packages().join("a").exists()); @@ -169,7 +169,7 @@ fn check_no_sync_uses_compatible_lock_interpreter() -> Result<()> { #[test] fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { - let server = PackseServer::new("local/local-simple.toml"); + let server = PackseServer::new("simple/single-package.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -178,7 +178,7 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+bar"] + dependencies = ["a==1.0.0"] "#})?; context .lock() @@ -194,7 +194,7 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+foo"] + dependencies = ["a==2.0.0"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -236,14 +236,14 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { [[package]] name = "a" - -version = "1.2.3+bar" - +version = "1.2.3+foo" + -version = "1.0.0" + +version = "2.0.0" source = { registry = "http://[LOCALHOST]/simple/" } - -sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+bar.tar.gz", hash = "sha256:b96640078f7011764f0e428c0481c726149e4ae3190d29f9f5f7c2372e11f074", upload-time = "2024-03-24T00:00:00Z" } - +sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+foo.tar.gz", hash = "sha256:d8c42806a0bf7b47451778ed2a909a2f9d22fe3a4bac673934fc8fb2537a886a", upload-time = "2024-03-24T00:00:00Z" } + -sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } + +sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - - { url = "http://[LOCALHOST]/files/a-1.2.3+bar-py3-none-any.whl", hash = "sha256:dc4f0c4f8461a32a72175670fa502b7bfb6c0e0ab542e8e26895caf77fc4b17d", upload-time = "2024-03-24T00:00:00Z" }, - + { url = "http://[LOCALHOST]/files/a-1.2.3+foo-py3-none-any.whl", hash = "sha256:2fd8eec176cad72b6c4372682485914aff65d215fb8cd71c85e9ed4511fa8bbe", upload-time = "2024-03-24T00:00:00Z" }, + - { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, + + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -255,8 +255,8 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { ] [package.metadata] - -requires-dist = [{ name = "a", specifier = "==1.2.3+bar" }] - +requires-dist = [{ name = "a", specifier = "==1.2.3+foo" }] + -requires-dist = [{ name = "a", specifier = "==1.0.0" }] + +requires-dist = [{ name = "a", specifier = "==2.0.0" }] "#); }); assert!(!context.site_packages().join("a").exists()); @@ -266,7 +266,7 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { #[test] fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { - let server = PackseServer::new("local/local-simple.toml"); + let server = PackseServer::new("simple/single-package.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -275,7 +275,7 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+bar"] + dependencies = ["a==1.0.0"] "#})?; context .lock() @@ -291,7 +291,7 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+foo"] + dependencies = ["a==2.0.0"] "#})?; uv_snapshot!( @@ -356,7 +356,7 @@ fn check_no_sync_locked_requires_existing_lock() -> Result<()> { #[test] fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { - let server = PackseServer::new("local/local-simple.toml"); + let server = PackseServer::new("simple/single-package.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -365,7 +365,7 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+bar"] + dependencies = ["a==1.0.0"] "#})?; context .lock() @@ -381,7 +381,7 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3+foo"] + dependencies = ["a==2.0.0"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 @@ -451,7 +451,7 @@ fn check_no_sync_frozen_requires_existing_lock() -> Result<()> { #[test] fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { - let server = PackseServer::new("local/local-simple.toml"); + let server = PackseServer::new("simple/single-package.toml"); let context = uv_test::test_context!("3.12"); context @@ -462,7 +462,7 @@ fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["a==1.2.3"] + dependencies = ["a==1.0.0"] "#})?; context.temp_dir.child("main.py").write_str(indoc! {r" x: int = 1 diff --git a/test/scenarios/simple/single-package.toml b/test/scenarios/simple/single-package.toml new file mode 100644 index 00000000000..e5e3b554e60 --- /dev/null +++ b/test/scenarios/simple/single-package.toml @@ -0,0 +1,16 @@ +name = "single-package" +description = "A single package with two stable versions." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "The latest version of 'a' should be selected." + +[expected.packages] +a = "2.0.0" + +[packages.a.versions."1.0.0"] + +[packages.a.versions."2.0.0"] From 2e682dbd0745bdb7e86986f5fcb77b445cad3f40 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 18 Jun 2026 19:07:51 +0100 Subject: [PATCH 3/3] Remove exclude-newer overrides from check tests --- crates/uv/tests/project/check.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/uv/tests/project/check.rs b/crates/uv/tests/project/check.rs index 3ce99d7867a..1295c0d6e1f 100644 --- a/crates/uv/tests/project/check.rs +++ b/crates/uv/tests/project/check.rs @@ -65,7 +65,6 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { .arg("--no-sync") .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -87,6 +86,9 @@ fn check_no_sync_creates_lock_without_sync() -> Result<()> { revision = 3 requires-python = ">=3.12" + [options] + exclude-newer = "2026-02-15T00:00:00Z" + [[package]] name = "a" version = "1.0.0" @@ -170,7 +172,7 @@ fn check_no_sync_uses_compatible_lock_interpreter() -> Result<()> { #[test] fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { let server = PackseServer::new("simple/single-package.toml"); - let context = uv_test::test_context!("3.12"); + let context = uv_test::test_context!("3.12").with_exclude_newer("2026-02-15T00:00:00Z"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(indoc! {r#" @@ -184,7 +186,6 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { .lock() .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -207,7 +208,6 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { .arg("--no-sync") .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -229,11 +229,14 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { assert_snapshot!(diff, @r#" --- old +++ new - @@ -1,23 +1,23 @@ + @@ -1,26 +1,26 @@ version = 1 revision = 3 requires-python = ">=3.12" + [options] + exclude-newer = "2026-02-15T00:00:00Z" + [[package]] name = "a" -version = "1.0.0" @@ -267,7 +270,7 @@ fn check_no_sync_updates_stale_lock_without_sync() -> Result<()> { #[test] fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { let server = PackseServer::new("simple/single-package.toml"); - let context = uv_test::test_context!("3.12"); + let context = uv_test::test_context!("3.12").with_exclude_newer("2026-02-15T00:00:00Z"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(indoc! {r#" @@ -281,7 +284,6 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { .lock() .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -301,8 +303,7 @@ fn check_no_sync_locked_rejects_stale_lock_without_update() -> Result<()> { .arg("--no-sync") .arg("--locked") .arg("--index") - .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER), + .arg(server.index_url()), @" success: false exit_code: 2 @@ -357,7 +358,7 @@ fn check_no_sync_locked_requires_existing_lock() -> Result<()> { #[test] fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { let server = PackseServer::new("simple/single-package.toml"); - let context = uv_test::test_context!("3.12"); + let context = uv_test::test_context!("3.12").with_exclude_newer("2026-02-15T00:00:00Z"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(indoc! {r#" @@ -371,7 +372,6 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { .lock() .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let stale_lock = context.read("uv.lock"); @@ -395,7 +395,6 @@ fn check_no_sync_frozen_uses_existing_lock_without_update() -> Result<()> { .arg("--frozen") .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -476,7 +475,6 @@ fn check_no_sync_isolated_does_not_write_lock_or_sync() -> Result<()> { .arg("--isolated") .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--ty-version") .arg("0.0.17"), @" @@ -981,7 +979,7 @@ fn check_with_declared_dependency() -> Result<()> { #[test] fn check_isolated() -> Result<()> { let server = PackseServer::new("extras/extra-does-not-exist-backtrack.toml"); - let context = uv_test::test_context!("3.12"); + let context = uv_test::test_context!("3.12").with_exclude_newer("2026-02-15T00:00:00Z"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(indoc! {r#" @@ -1028,7 +1026,6 @@ fn check_isolated() -> Result<()> { .lock() .arg("--index") .arg(server.index_url()) - .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); let existing_lock = context.read("uv.lock");