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