@@ -8473,3 +8473,97 @@ fn prune_cache_url_subdirectory() -> Result<()> {
8473
8473
8474
8474
Ok ( ( ) )
8475
8475
}
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