@@ -8473,3 +8473,97 @@ fn prune_cache_url_subdirectory() -> Result<()> {
84738473
84748474 Ok ( ( ) )
84758475}
8476+
8477+ /// Test that incoherence in the versions in a package entry of the lockfile versions is caught.
8478+ ///
8479+ /// See <https://github.com/astral-sh/uv/issues/12164>
8480+ #[ test]
8481+ fn locked_version_coherence ( ) -> Result < ( ) > {
8482+ let context = TestContext :: new ( "3.12" ) ;
8483+
8484+ let pyproject_toml = context. temp_dir . child ( "pyproject.toml" ) ;
8485+ pyproject_toml. write_str (
8486+ r#"
8487+ [project]
8488+ name = "project"
8489+ version = "0.1.0"
8490+ requires-python = ">=3.12"
8491+ dependencies = ["iniconfig"]
8492+ "# ,
8493+ ) ?;
8494+
8495+ uv_snapshot ! ( context. filters( ) , context. lock( ) , @r"
8496+ success: true
8497+ exit_code: 0
8498+ ----- stdout -----
8499+
8500+ ----- stderr -----
8501+ Resolved 2 packages in [TIME]
8502+ " ) ;
8503+
8504+ let lock = context. read ( "uv.lock" ) ;
8505+
8506+ insta:: with_settings!( {
8507+ filters => context. filters( ) ,
8508+ } , {
8509+ assert_snapshot!(
8510+ lock, @r#"
8511+ version = 1
8512+ revision = 1
8513+ requires-python = ">=3.12"
8514+
8515+ [options]
8516+ exclude-newer = "2024-03-25T00:00:00Z"
8517+
8518+ [[package]]
8519+ name = "iniconfig"
8520+ version = "2.0.0"
8521+ source = { registry = "https://pypi.org/simple" }
8522+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
8523+ wheels = [
8524+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
8525+ ]
8526+
8527+ [[package]]
8528+ name = "project"
8529+ version = "0.1.0"
8530+ source = { virtual = "." }
8531+ dependencies = [
8532+ { name = "iniconfig" },
8533+ ]
8534+
8535+ [package.metadata]
8536+ requires-dist = [{ name = "iniconfig" }]
8537+ "# ) ;
8538+ } ) ;
8539+
8540+ // Write an inconsistent iniconfig entry
8541+ context
8542+ . temp_dir
8543+ . child ( "uv.lock" )
8544+ . write_str ( & lock. replace ( r#"version = "2.0.0""# , r#"version = "1.0.0""# ) ) ?;
8545+
8546+ // An inconsistent lockfile should fail with `--locked`
8547+ uv_snapshot ! ( context. filters( ) , context. sync( ) . arg( "--locked" ) , @r"
8548+ success: false
8549+ exit_code: 2
8550+ ----- stdout -----
8551+
8552+ ----- stderr -----
8553+ error: Failed to parse `uv.lock`
8554+ Caused by: Locked package and file versions are inconsistent for `iniconfig`
8555+ " ) ;
8556+
8557+ // Without `--locked`, we could fail or recreate the lockfile, currently, we fail.
8558+ uv_snapshot ! ( context. filters( ) , context. lock( ) , @r"
8559+ success: false
8560+ exit_code: 2
8561+ ----- stdout -----
8562+
8563+ ----- stderr -----
8564+ error: Failed to parse `uv.lock`
8565+ Caused by: Locked package and file versions are inconsistent for `iniconfig`
8566+ " ) ;
8567+
8568+ Ok ( ( ) )
8569+ }
0 commit comments