Repository: mitsuhiko/insta (rust)

Issue:
Setting `test.unreferenced` in yaml config file doesn't seem to do anything.
### What happened?

I created a basic `insta.yaml`:
```yaml
test:
  unreferenced: reject
```
Running `cargo insta test --unreferenced reject` fails with one unreferenced snapshot, but running `cargo insta test` does not.  In other words, `cargo insta test` doesn't appear to be using the value from the config file.

### Reproduction steps

1. Create `insta.yaml` w/ `test.unreferenced` set to `reject`.
2. Setup an unreferenced snapshot in the workspace.
3. Run `cargo insta test` with and without `--unreferenced reject` and observe different behavior.


### Insta Version

1.42.2

### rustc Version

1.85.0

### What did you expect?

If `insta.yaml` sets `test.unresolved` to `reject` then I expect running `cargo insta test` to behave the same as `cargo insta test --unresolved reject`.

### Additional notes:

* I have also tried with values other than `reject` and it still doesn't work.
* I know that it's finding and parsing my config file because if I try to set `test.unreferenced` to an invalid value like `foobar` then I get an error about it when running `cargo insta`.
* You should consider adding `test.unreferenced` to [the list of documented config file options](https://insta.rs/docs/settings/) (at the bottom of the page).  I only discovered it by guessing that the `--unreferenced` command line parameter (which is at least [documented here](https://insta.rs/docs/cli/)) might fall back to a config file setting, but I had to dig through the code to discover how that setting is spelled in the config file.
* I didn't see any obvious culprits in the [PR that added the setting](https://github.com/mitsuhiko/insta/pull/328), but I only glanced at it and don't know the code well enough (nor how it might have changed since then) to easily dig much deeper.  It seems possible that it could just be a silly mistake that hasn't been caught yet, though, since there probably aren't many people trying to use an undocumented (as far as I can tell) config file setting.

Pull request description:
fix `unreferenced` not being read from config file
closes #757


Patch:
diff --git a/.typos.toml b/.typos.toml
index f7dddbc..fa5e8be 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -1,2 +1,2 @@
 [default]
-extend-words = { "Foled" = "Foled" }
\ No newline at end of file
+extend-words = { "Foled" = "Foled" }
diff --git a/cargo-insta/src/cli.rs b/cargo-insta/src/cli.rs
index 0fc7277..bcea5e1 100644
--- a/cargo-insta/src/cli.rs
+++ b/cargo-insta/src/cli.rs
@@ -197,8 +197,8 @@ struct TestCommand {
     #[arg(long)]
     force_update_snapshots: bool,
     /// Handle unreferenced snapshots after a successful test run.
-    #[arg(long, default_value = "ignore")]
-    unreferenced: UnreferencedSnapshots,
+    #[arg(long)]
+    unreferenced: Option<UnreferencedSnapshots>,
     /// Filters to apply to the insta glob feature.
     #[arg(long)]
     glob_filter: Vec<String>,
@@ -742,9 +742,14 @@ fn test_run(mut cmd: TestCommand, color: ColorWhen) -> Result<(), Box<dyn Error>
     // Legacy command
     if cmd.delete_unreferenced_snapshots {
         eprintln!("Warning: `--delete-unreferenced-snapshots` is deprecated. Use `--unreferenced=delete` instead.");
-        cmd.unreferenced = UnreferencedSnapshots::Delete;
+        cmd.unreferenced = Some(UnreferencedSnapshots::Delete);
     }
 
+    // If unreferenced wasn't specified, use the config file setting
+    cmd.unreferenced = cmd
+        .unreferenced
+        .or_else(|| Some(loc.tool_config.test_unreferenced()));
+
     // Prioritize the command line over the tool config
     let test_runner = match cmd.test_runner {
         TestRunner::Auto => loc.tool_config.test_runner(),
@@ -797,7 +802,12 @@ fn test_run(mut cmd: TestCommand, color: ColorWhen) -> Result<(), Box<dyn Error>
     // tests ran successfully
     if success {
         if let Some(ref snapshot_ref_path) = snapshot_ref_file {
-            handle_unreferenced_snapshots(snapshot_ref_path.borrow(), &loc, cmd.unreferenced)?;
+            handle_unreferenced_snapshots(
+                snapshot_ref_path.borrow(),
+                &loc,
+                // we set this to `Some` above so can't be `None`
+                cmd.unreferenced.unwrap(),
+            )?;
         }
     }
 
@@ -996,7 +1006,7 @@ fn prepare_test_runner<'snapshot_ref>(
     proc.env("INSTA_CARGO_INSTA", "1");
     proc.env("INSTA_CARGO_INSTA_VERSION", cargo_insta_version());
 
-    let snapshot_ref_file = if cmd.unreferenced != UnreferencedSnapshots::Ignore {
+    let snapshot_ref_file = if cmd.unreferenced != Some(UnreferencedSnapshots::Ignore) {
         match snapshot_ref_file {
             Some(path) => Some(Cow::Borrowed(path)),
             None => {


Test patch:
diff --git a/cargo-insta/tests/functional/main.rs b/cargo-insta/tests/functional/main.rs
index d67f1dd..66f75fd 100644
--- a/cargo-insta/tests/functional/main.rs
+++ b/cargo-insta/tests/functional/main.rs
@@ -889,3 +889,137 @@ src/
         stderr
     );
 }
+
+#[test]
+fn test_unreferenced_config_reject() {
+    // This test verifies that the `test.unreferenced: reject` setting in insta.yaml
+    // is respected when no command-line argument is provided.
+    //
+    // Specifically, it tests the fix for issue #757, which ensures that:
+    // 1. Config file settings are properly applied when not overridden by command-line flags
+    // 2. Error handling for unreferenced snapshots properly updates the success flag
+    let test_project = TestFiles::new()
+        .add_cargo_toml("test_unreferenced_config_reject")
+        .add_file(
+            "src/lib.rs",
+            r#"
+#[test]
+fn test_snapshot() {
+    insta::assert_snapshot!("Hello, world!");
+}
+"#
+            .to_string(),
+        )
+        .create_project();
+
+    // Run tests to create snapshots first (without the config file)
+    let output = test_project
+        .insta_cmd()
+        .args(["test", "--accept"])
+        .output()
+        .unwrap();
+
+    assert!(output.status.success());
+
+    // Now add the config file after snapshot is created
+    test_project.update_file(
+        "insta.yaml",
+        r#"
+test:
+  unreferenced: reject
+"#
+        .to_string(),
+    );
+
+    // Manually add an unreferenced snapshot
+    let unreferenced_snapshot_path = test_project
+        .workspace_dir
+        .join("src/snapshots/test_unreferenced_config_reject__unused_snapshot.snap");
+    std::fs::create_dir_all(unreferenced_snapshot_path.parent().unwrap()).unwrap();
+    std::fs::write(
+        &unreferenced_snapshot_path,
+        r#"---
+source: src/lib.rs
+expression: "Unused snapshot"
+---
+Unused snapshot
+"#,
+    )
+    .unwrap();
+
+    // Verify files exist
+    let snapshot_path = test_project
+        .workspace_dir
+        .join("src/snapshots/test_unreferenced_config_reject__snapshot.snap");
+    let unreferenced_path = test_project
+        .workspace_dir
+        .join("src/snapshots/test_unreferenced_config_reject__unused_snapshot.snap");
+
+    assert!(snapshot_path.exists(), "Normal snapshot file should exist");
+    assert!(
+        unreferenced_path.exists(),
+        "Unreferenced snapshot file should exist"
+    );
+
+    // First verify explicitly passing --unreferenced=reject does fail correctly
+    let output = test_project
+        .insta_cmd()
+        .args(["test", "--unreferenced=reject", "--", "--nocapture"])
+        .stderr(Stdio::piped())
+        .output()
+        .unwrap();
+
+    // The test should fail with explicit flag
+    assert!(
+        !output.status.success(),
+        "Command should fail with explicit --unreferenced=reject flag"
+    );
+
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    assert!(
+        stderr.contains("encountered unreferenced snapshots"),
+        "Expected error message about unreferenced snapshots, got: {}",
+        stderr
+    );
+
+    // Now run without flags - this should also fail due to the config file setting
+    let output = test_project
+        .insta_cmd()
+        .args(["test", "--", "--nocapture"])
+        .stderr(Stdio::piped())
+        .output()
+        .unwrap();
+
+    // The command should fail because of the config file setting
+    assert!(
+        !output.status.success(),
+        "Command should fail when config file has test.unreferenced: reject"
+    );
+
+    // Verify the error message mentions unreferenced snapshots
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    assert!(
+        stderr.contains("encountered unreferenced snapshots"),
+        "Expected error message about unreferenced snapshots, got: {}",
+        stderr
+    );
+
+    // Run with --unreferenced=delete to clean up
+    let output = test_project
+        .insta_cmd()
+        .args(["test", "--unreferenced=delete", "--", "--nocapture"])
+        .output()
+        .unwrap();
+
+    assert!(output.status.success());
+
+    // Verify the unreferenced snapshot was deleted
+    assert!(
+        snapshot_path.exists(),
+        "Normal snapshot file should still exist"
+    );
+    assert!(
+        !unreferenced_path.exists(),
+        "Unreferenced snapshot file should have been deleted"
+    );
+}